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. Conventional linear regulators, such as the 78XX series of chips, require an input voltage higher than 2v~3v above the output voltage. Otherwise it will not work properly. However, in some cases, this condition is obviously too harsh, such as 5v to 3.3v, the input and output pressure difference is only 1.7v. Obviously, the conditions are not met. In response to this situation. Has the Ldo class power conversion chip.

The Ldo is suitable for voltage requirements, but the power is not very large equipment.

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 a regulator will hang here every single one of them.

Regulator_map_list Global variables A consumer will hang here every single one of them.

To write driver-driven steps

Overview

Most of the PMU drives and regulator drivers in the kernel are mixed together. Very bad. For example, put Regulator_init_data in platform's driver data.

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. Mtcmos is also the PMU all the way to power supply as a parent. But Mtcmos is the power supply line on the SOC chip.

For example, Mtcmos uses PMU to power the buck. Usb,sd,pcie, however, used a 3-way mtcmos for power.

Each power supply node is a regulator.

Assume that the mtcmos situation is not considered. Usually one-tier power supply.

In other words, from the PMU out of a regulator below the hook is consumer. Instead of multiple regulator cascade.

Assuming cascading regulator, you can set the parent regulator in the Supply_name field of the Regulator_desc.

The recommended wording is. Regulator itself with what substance does not have platform Dev and driver register, in probe register regulator. Only regulator OPS will invoke the real PMU driver.

This realizes the principle of adaptive layer and detailed drive separation.

Define your own regulator ID format

Regulator_desc has a member that is an ID. This ID has no effect in distinguishing between different regulator, since regulator are all found and differentiated by the name string. The set of IDs is now in the 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 generally 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 according to Func[pmu_id][offset] two-bit array.

These usages 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 is typically up to dozens of-way power supply. But the number of OPS is not large. A common class of devices with a set of OPS functions, the usual categories are Buck Ops and Ldo OPS. The OPS function is in (struct Regulator_dev *), so it is possible to get detailed ID information based on Regulator_dev and perform different register operations.

int offset = rdev_get_id (Rdev);

Theoretically, a PMU full of regulator can use the same set of OPS, and then distinguish operations with IDs. There is also an extreme of each regulator with a different set of OPS. Just for code multiplexing and decoupling, typically a class of regulator with a set of OPS.

Examples:

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 Sample:

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 to be used. As in the above code "Vdet". So the assumption is that the code of the series SOC, preferably this name can not be with the PMU chip model or SOC model, due to the same module, such as USB controller. It is very likely that the series on the Soc are the same, and the drive is multiplexed. It's not possible to fit the regulator every other soc. So it's better to name "Myregulator-usb" in such a case.

This changes the PMU or SOC, only with Regulator_get (Dev, "myregulator-usb") can always adapt to various situations, there is no need to change the drive.

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,

}, {

Another way of writing. Ability to use defined enumerations as index to fill in the array in order.

Just to regulator_desc arrays and regulator_init_data arrays to 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 put regulator_init_data into Platform_data. Not recommended.

The regulator should be separated from the PMU driver.

Just need to invoke the PMU operation function in OPS.

Regulator_init_data should also be an array of structs. Matches 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 the following, for example.

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 will be able to register the regulator in the probe only.

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),

};

Register device and driver in Module_init to be able to.

Platform_device_register (&my_regulator_dev)

Platform_driver_register (&my_regulator_driver);

Regulator use

The whole point is to call the name of the Regulator_get power node first, get the regulator pointer, and then call the OPS function set.

The proportions are as follows:

Regulator = regulator_get (Dev, "My_usb").

It will return the USB name-powered regulator.

Open and close the Calibrator (regulator) API for example below.

int regulator_enable (regulator);

int regulator_disable (regulator);

There are some 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 brochure regulator, including settings 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 be able to

* @of_node:d Evice tree can be set to 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 driver just to provide REGULATOR_DESC and regulator_init_data to be able to allocate 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);

Assuming 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 in Regulator_list to find, if found, add this regulator to the superior consumer_list

R =regulator_dev_lookup (Dev, supply);

Ret= set_supply (Rdev, R);

...

}

/*

First check that there are no conflicts or iterations in the Regulator_map_list global variables. Assuming that no struct REGULATOR_MAP is applied for consumer,

Then set the regulator of Regulator_map to this regulator. At the same time using Regulator_map to record 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);

}

Regulator_desc and Regulator_init_data name repeating fields

The name of the regulator is defined in the Constraints->name field of the struct regulator_init_data and can be defined in the Regulator_desc name field. But Constraints->name has a higher priority.

regulator_register-"regulator_dev_lookup-" Rdev_get_name code such as the following:

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

Except for name. Regulator_desc->supply_name and Regulator_init_data->supply_regulator are also repeated, are pointers to the superior regulator, Regulator_init _data has a higher priority and can see the code in Regulator_register such as the following

if (Init_data &&init_data->supply_regulator)

supply= init_data->supply_regulator;

else if (regulator_desc->supply_name)

supply= regulator_desc->supply_name;


Linux kernel regulator architecture and authoring

Related Article

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.