Linux kernel regulator architecture and authoring

Source: Internet
Author: User
Tags switch case

Power Type Introduction

(Baidu Encyclopedia) Ldo is low dropout regulator, meaning for the lower dropout linear regulator, is relative to the traditional linear regulator. Traditional linear regulators, such as the 78XX series of chips are required to input voltage higher than the output voltage above 2v~3v, otherwise it will not work properly. However, in some cases, such conditions are obviously too harsh, such as 5v to 3.3v, the input and output of the pressure difference is only 1.7v, obviously is not satisfied with the conditions. In this case, there is the Ldo class power conversion chip. The Ldo is suitable for voltage requirements, but is not very powerful.

Buck Circuit, step-down converter circuit. is a DC-to-D.C. converter, the simple is to convert a DC voltage through the oscillation circuit to a high-frequency power supply, and then through the pulse transformer, rectifier filter circuit output required DC voltage, similar to switching power supply

Data

Structregulator_dev {

Structregulator_desc *desc;

Structlist_head list; Regulator is linked to the regulator_list linked list through this structure

Structlist_head consumer_list; This regulator is responsible for powering the list of devices

Structregulation_constraints *constraints;

Structregulator *supply; Pointer to parent regulator

};

Regulator_list Global variables Each registration of a regulator will hang here.

Regulator_map_list Global variables each registration of a consumer will hang here.

To write driver-driven steps

Overview

In the kernel, the PMU drive and the regulator drive are mostly mixed together to write, very bad. For example, put Regulator_init_data in platform's driver data to pass in.

Generally speaking, a SOC will have a limited number of PMU. Regulator as the abstraction layer of PMU. The power supply line is divided into PMU direct power supply and Mtcmos power supply. The Mtcmos is also the PMU's way of powering the parent power supply, but the Mtcmos is the power supply line on the SOC chip. For example, Mtcmos with PMU of all the way buck power supply, but Usb,sd,pcie used 3 Mtcmos to power supply respectively.

Each power supply node is a regulator. If the mtcmos situation is not considered, it is usually a primary power supply. That is to say, from the PMU out of a regulator under the hook is consumer, instead of multiple regulator cascade.

If cascading regulator are present, you can set the parent regulator in the Supply_name field of the Regulator_desc.

The recommended notation is that regulator itself is registered with platform Dev and driver without any substance and registered regulator in probe. Only the regulator OPS will invoke the real PMU driver. This realizes the principle of adaptation layer and specific drive separation.

Custom Regulator ID Format

Regulator_desc has a member that is an ID. This ID has no effect in distinguishing between different regulator, because regulator are found and differentiated by the name string. The set of IDs is represented in an OPS function pointer array. A PMU chip typically has multiple power supplies, but a class of power supplies (such as Buck) using the same set of OPS function pointer arrays are usually the same. However, there are differences in the registers and methods of the actual different regulator settings. So the distinction is made by ID. The ID is usually used as offset. Registers and operations are placed in an array so that information is obtained from the array using the ID, avoiding the use of a large number of if and switch case statements.

struct REGULATOR_DESC {

Const Char*name;

Const Char*supply_name;

int id;

...

};

Some of the more optimized methods can be that some bits of the ID represent the index of information for the PMU or regulator itself, while others indicate an offset. In this way, multiple PMU chips in a series may be reused with the same set of regulator adaptation layer codes. Operation information can be performed in Func[pmu_id][offset] two-bit arrays. These methods of use are flexible.

function to get ID

int rdev_get_id (struct Regulator_dev *rdev)

{

returnrdev->desc->id;

}

Step1 Prepare Ops

Although the arm SOC PMU typically has up to dozens of power supplies, but the number of OPS is not large, the general class of devices with a set of OPS functions, the common categories are Buck Ops and Ldo OPS. The entry parameter of the OPS function is (struct Regulator_dev *), so the specific ID information can be obtained according to Regulator_dev, and different register operation is performed.

int offset = rdev_get_id (Rdev);

Theoretically, all regulator of a PMU can be operated with the same set of OPS and then distinguished by ID. The other extreme is each regulator with a different set of OPS. But for code reuse and decoupling, it is usually a class of regulator with a set of OPS.

Example:

Static Structregulator_ops Max8660_ldo5_ops = {

. List_voltage = Max8660_ldo5_list,

. Set_voltage = Max8660_ldo5_set,

. Get_voltage = Max8660_ldo5_get,

};

Step2 Preparation Consumer

code example:

static struct regulator_consumer_supplylp3974_buck3_consumer[] = {

Regulator_supply ("Vdet", "s5p-sdo"),

Regulator_supply ("Vdd_reg", "0-003c"),

};

Consumer refers to the leaf nodes on the regulator tree. The regulator_consumer_supply array is used as a member of the Regulator_init_data.

Consumer's name is required for each module, such as the "Vdet" of the code above. So if the code for a series of Soc, the best name can not be with the PMU chip model or SOC model, because the same module, such as the USB controller, it is likely that the series on the SOC is the same, the driver is also multiplexed. It's not possible to fit the regulator every other soc. So it's best to name "Myregulator-usb" in this case. This changes the PMU or SOC, as long as the Regulator_get (Dev, "myregulator-usb") can always adapt to various situations, there is no need to modify the driver.

STEP3 Preparation Regulator_desc

Note that when Regulator_register, Regulator_desc and Regulator_init_data are one by one matches. So the order of the Regulator_desc array and the Regulator_init_data array should be consistent.

Static Structregulator_desc regulators[] = {

{

. Name = "LDO2",

. id = max8998_ldo2,

. Ops = &max8998_ldo_ops,

. Type =regulator_voltage,

. Owner = This_module,

}, {

. Name = "LDO3",

. id = Max8998_ldo3,

. Ops = &max8998_ldo_ops,

. Type = Regulator_voltage,

. Owner = This_module,

}, {

There is also a way to write an array by using a defined enumeration as index, in order. As long as both the Regulator_desc array and the Regulator_init_data array are index with the same enumeration, that must be one by one matches.

Static Structregulator_desc regulators[] = {

[REG0] = {

. Name = "LDO2",

. id = max8998_ldo2,

. Ops = &max8998_ldo_ops,

. Type = Regulator_voltage,

. Owner = This_module,

},

[REG1] = {

. Name = "LDO3",

. id = Max8998_ldo3,

. Ops = &max8998_ldo_ops,

. Type = Regulator_voltage,

. Owner = This_module,

}, {

STEP4 Preparation Regulator_init_data

Some drivers will regulator_init_data put into Platform_data, not recommended. The regulator should be separated from the PMU driver. Only the operation function of PMU should be called in OPS.

Regulator_init_data should also be an array of structs, matching the Regulator_desc struct array one by one.

Note that the name field of constraints has a higher priority than REGULATOR_DESC when it looks for regulator. It is not recommended to write name in constraints.

One of these is as follows.

static struct Regulator_init_data lp3974_buck3_data ={

. constraints = {

. Name = "vcc_1.8v",

. Min_uv = 1800000,

. Max_uv = 1800000,

. Apply_uv = 1,

. always_on = 1,

. State_mem = {

. Enabled = 1,

},

},

. num_consumer_supplies = Array_size (Lp3974_buck3_consumer),

. consumer_supplies = Lp3974_buck3_consumer,

};

STEP5 Ready to register regulator

The simplest platform registration can be, only need to probe in the regulator are registered.

static int my_regulator_probe (struct Platform_device*pdev)

{

int i;

for (i = 0;i < MAX; i + +)

{

Regulator_register (&regulator_desc[i], &pdev->dev,regulator_init_data[i], NULL, NULL);

}

}

static struct Platform_device My_regulator_dev = {

. Name = "My_regulator",

};

static struct Platform_driver My_regulator_driver = {

. Driver ={

. Name= "My_regulator",

. owner= This_module,

},

. Probe =my_regulator_probe,

. Remove =__devexit_p (my_regulator__remove),

};

Registering device and driver in Module_init is possible.

Platform_device_register (&my_regulator_dev)

Platform_driver_register (&my_regulator_driver);

Regulator use

In general, first call the name of the Regulator_get power node, get the regulator pointer, and then call the OPS function set. Examples are as follows:

Regulator = regulator_get (Dev, "my_usb");

It will return the USB name-powered regulator.

Turn on and off the Calibrator (regulator) API as follows.

int regulator_enable (regulator);

int regulator_disable (regulator);

There are other functions, see struct Regulator_ops. Common with

struct Regulator_ops {

/* Get/setregulator voltage */

Int (*set_voltage) (struct Regulator_dev *, int min_uv, int max_uv,

unsigned *selector);

Int (*get_voltage) (struct Regulator_dev *);

/* Get/setregulator Current */

Int (*set_current_limit) (struct Regulator_dev *,

int Min_ua, int max_ua);

Int (*get_current_limit) (struct Regulator_dev *);

/*enable/disable Regulator */

Int (*enable) (struct Regulator_dev *);

Int (*disable) (struct Regulator_dev *);

Int (*is_enabled) (struct Regulator_dev *);

};

Kernel Code Analysis registers regulator, including setting supply and consumer

/**

*regulator_register-register Regulator

* @regulator_desc: It's regulator_desc.

* @dev:

* @init_data: It's regulator_init_data.

* @driver_data: User private information, not recommended, set to NULL to

* @of_node:d Evice tree can be set regulator, not first).

*/

struct Regulator_dev *regulator_register (Structregulator_desc *regulator_desc,

Structdevice *dev, const struct Regulator_init_data *init_data,

Void*driver_data, struct Device_node *of_node)

{

Write the drive as long as the Regulator_desc and Regulator_init_data are available, assigning the REGULATOR_DEV structure here

Rdev =kzalloc (sizeof (struct regulator_dev), gfp_kernel);

Initializing the REGULATOR_DEV structure

Set constraints

RET =set_machine_constraints (Rdev, constraints);

If this regulator has a parent regulator, set the parent regulator. Priority Selection Init_data

if (Init_data && init_data->supply_regulator)

supply= init_data->supply_regulator;

else if (regulator_desc->supply_name)

supply= regulator_desc->supply_name;

if (supply) {

Use the name string to find the regulator_list, and if found, add the regulator to the upper Consumer_list

R =regulator_dev_lookup (Dev, supply);

Ret= set_supply (Rdev, R);

...

}

/*

First check that there are no conflicts or duplicates in the Regulator_map_list global variables. If no struct regulator_map is requested for consumer,

Then set REGULATOR_MAP regulator set to this regulator, while using REGULATOR_MAP to record the parent-child information,

and add the Regulator_map to the regulator_map_list global variable.

*/

if (init_data) {

for (i = 0; i < init_data->num_consumer_supplies; i++) {

Ret= set_consumer_device_supply (Rdev,

Init_data->consumer_supplies[i].dev_name,

init_data->consumer_supplies[i].supply);

}

}

Add this regulator to the global regulator_list

List_add (&rdev->list,&regulator_list);

}

Name repeating fields for Regulator_desc and Regulator_init_data

The name of the regulator can be defined in the Constraints->name field of the struct regulator_init_data, or it can be defined in the Regulator_desc name field. However, the constraints->name has a higher priority.

regulator_register-"regulator_dev_lookup-" Rdev_get_name code is as follows:

static const char *rdev_get_name (struct Regulator_dev*rdev)

{

if (rdev->constraints && rdev->constraints->name)

returnrdev->constraints->name;

else if (rdev->desc->name)

returnrdev->desc->name;

Else

Return "";

}

Static List_head (regulator_list); All regulator are registered in this list

Advanced Power supply repeating field

In addition to Name,regulator_desc->supply_name and Regulator_init_data->supply_regulator is also repeated, are pointing to the superior regulator pointers, Regulator_init_data higher priority, you can see the code in the Regulator_register as follows

if (Init_data &&init_data->supply_regulator)

supply= init_data->supply_regulator;

else if (regulator_desc->supply_name)

supply= regulator_desc->supply_name;


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.