一:GPIO
//dtsi
device {
rst-gpio = <&tlmm 67 0x00>;
};
// sample code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
void device_probe(struct platform_device *pdev)
{
struct device *dev;
struct device_node *np;
int rst_gpio;
int rc;
probe:
dev = &pdev->dev;
np = &pdev->dev.of_node;
//dev = &client->dev;
//np = client->dev.of_node; // i2c
rst_gpio = of_get_named_gpio(np, "rst-gpio", 0);
//rst_gpio = of_get_named_gpio_flags(np, "rst-gpio", 0, &flags);
init:
if (gpio_is_valid(rst_gpio)) {
rc = devm_gpio_request_one(dev, pdata->enable_gpio,
GPIOF_OUT_INIT_LOW, "xxx_reset");
if (rc < 0) {
pr_err("rst gpio request failed\n");
}
}
use:
if (gpio_is_valid(rst_gpio)) {
gpio_set_value(rst_gpio, 0);
usleep_range(1000, 2000); /* Keep enable down at least 1ms */
gpio_set_value(rst_gpio, 1);
usleep_range(500, 1000); /* #include <linux/delay.h> */
}
deinit:
if (gpio_is_valid(rst_gpio)) {
gpio_set_value(rst_gpio, 0);
}
}
//API
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label);
int gpio_get_value(unsigned int gpio);
void gpio_set_value(unsigned int gpio, int value);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
bool gpio_is_valid(int number);
int gpio_to_irq(unsigned int gpio);
# 使用需要显示free资源
int gpio_request(unsigned gpio, const char *label);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_free(unsigned gpio);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);
二:IRQ
//dtsi
device {
qcom,platform-te-gpio = <&tlmm 23 0>;
}
// sample code
#include <linux/interrupt.h>
static irqreturn_t dsi_display_panel_te_irq_handler(int irq, void *data)
{
struct dsi_display *display = (struct dsi_display *)data;
...
...
return IRQ_HANDLED;
}
void device_irq_init(struct platform_device *pdev)
{
struct device *dev;
struct device_node *np;
int disp_te_gpio;
unsigned int te_irq;
int flags;
int rc;
dev = &pdev->dev;
np = = pdev->dev.of_node;
disp_te_gpio = of_get_named_gpio(np,
"qcom,platform-te-gpio", 0);
//disp_te_gpio = of_get_named_gpio_flags(np, "rst-gpio", 0, &flags);
if (!gpio_is_valid(disp_te_gpio)) {
rc = -EINVAL;
goto error;
}
rc = devm_gpio_request_one(dev, pdata->enable_gpio, GPIOF_IN, "xxx_irq");
if (rc < 0) {
pr_err("rst gpio request failed\n");
}
te_irq = gpio_to_irq(disp_te_gpio);
rc = devm_request_irq(dev, te_irq, dsi_display_panel_te_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"TE_GPIO", display);
error:
return;
}
//API
disable_irq(te_irq);
enable_irq(te_irq);
# handler:上半部处理函数,thread_fn:下半部处理函数
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn,
unsigned long irqflags, const char *devname,
void *dev_id);
三:regulator
// dtsi
device {
vdd-supply = <&pm8350_l6>;
}
// sample code
#include <linux/regulator/consumer.h>
void device_regulator_sample(struct platform_device *pdev)
{
struct device *dev;
struct regulator *vdd_reg;
int ret;
dev = &pdev->dev;
vdd_reg = devm_regulator_get(dev, "vdd");
if (IS_ERR(vdd_reg)) {
ret = PTR_ERR(vdd_reg);
pr_err("Failed to get vdd regulator: %d\n", ret);
//return ret;
}
// enable
if (vdd_reg && !regulator_is_enabled(vdd_reg)) {
ret = regulator_enable(axera_sdio->dvdd3_3);
if (ret < 0) {
pr_err("Couldn't enable vdd_reg regulator ret=%d\n",ret);
}
}
// disbale
if (vdd_reg && regulator_is_enabled(vdd_reg)) {
ret = regulator_disable(vdd_reg);
if (ret < 0) {
pr_err("Couldn't disable vdd_reg regulator ret=%d\n",ret);
}
}
}
//API
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
四:CLK
//dtsi
device {
clock-names = "bb_clk3";
clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
}
//sample code
#include <linux/clk.h>
void device_clk_init(struct platform_device *pdev)
{
struct device *dev;
struct clk *bb_clk;
int ret;
dev = &pdev->dev;
bb_clk = devm_clk_get(dev, "bb_clk3");
if (IS_ERR(bb_clk)) {
if (PTR_ERR(bb_clk) != -EPROBE_DEFER)
pr_err("Unable to get bb_clk3 clock\n");
return;
}
ret = clk_prepare_enable(bb_clk);
if (ret)
pr_err("enable bb_clk failed\n");
ret = clk_disable_unprepare(bb_clk);
if (ret)
pr_err("disable bb_clk failed\n");
}
//API
int clk_set_rate(struct clk *clk, unsigned long rate);
unsigned long clk_get_rate(struct clk *clk);
五:debugfs
// sample code
#include <linux/debugfs.h>
static int cam_ife_set_csid_debug(void *data, u64 val)
{
g_ife_hw_mgr.debug_cfg.csid_debug = val;
CAM_DBG(CAM_ISP, "Set CSID Debug value :%lld", val);
return 0;
}
static int cam_ife_get_csid_debug(void *data, u64 *val)
{
*val = g_ife_hw_mgr.debug_cfg.csid_debug;
CAM_DBG(CAM_ISP, "Get CSID Debug value :%lld",
g_ife_hw_mgr.debug_cfg.csid_debug);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(cam_ife_csid_debug,
cam_ife_get_csid_debug,
cam_ife_set_csid_debug, "%llu\n");
static int cam_ife_hw_mgr_debug_register(void)
{
int rc = 0;
struct dentry *dbgfileptr = NULL;
dbgfileptr = debugfs_create_dir("camera_ife", NULL);
if (!dbgfileptr) {
CAM_ERR(CAM_ISP,"DebugFS could not create directory!");
rc = -ENOENT;
goto end;
}
/* Store parent inode for cleanup in caller */
g_ife_hw_mgr.debug_cfg.dentry = dbgfileptr;
dbgfileptr = debugfs_create_file("ife_csid_debug", 0644,
g_ife_hw_mgr.debug_cfg.dentry, NULL, &cam_ife_csid_debug);
dbgfileptr = debugfs_create_u32("enable_recovery", 0644,
g_ife_hw_mgr.debug_cfg.dentry,
&g_ife_hw_mgr.debug_cfg.enable_recovery);
dbgfileptr = debugfs_create_bool("enable_req_dump", 0644,
g_ife_hw_mgr.debug_cfg.dentry,
&g_ife_hw_mgr.debug_cfg.enable_req_dump);
if (IS_ERR(dbgfileptr)) {
if (PTR_ERR(dbgfileptr) == -ENODEV)
CAM_WARN(CAM_ISP, "DebugFS not enabled in kernel!");
else
rc = PTR_ERR(dbgfileptr);
}
end:
g_ife_hw_mgr.debug_cfg.enable_recovery = 0;
return rc;
}
static int cam_ife_hw_mgr_debug_deinit(void)
{
debugfs_remove_recursive(g_ife_hw_mgr.debug_cfg.dentry);
g_ife_hw_mgr.debug_cfg.dentry = NULL;
}
六:DEVICE_ATTR
/示例:
static ssize_t show_test(struct device *dev, struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE,
"#Write into test to start test sequence!#\n");
}
static ssize_t store_test(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client;
int ret;
client = to_i2c_client(dev);
/*test */
ret = blinkm_test_run(client);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test);
static struct attribute *blinkm_attrs[] = {
//&dev_attr_red.attr,
//&dev_attr_green.attr,
//&dev_attr_blue.attr,
//&dev_attr_test.attr,
NULL,
};
probe:
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
if (err < 0) {
dev_err(&client->dev, "couldn't register sysfs group\n");
goto exit;
}
remove:
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
//sysfs.txt
结构体:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
声明:
DEVICE_ATTR(_name, _mode, _show, _store);
增/删属性:
int device_create_file(struct device *dev, const struct device_attribute *attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
七:DRIVER_ATTR
// 示例1:
static ssize_t version_show(struct device_driver *driver, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
}
static DRIVER_ATTR_RO(version);
static int sample_create_driver_attrs(struct device_driver *driver)
{
return driver_create_file(driver, &driver_attr_version);
}
static void sample_remove_driver_attrs(struct device_driver *driver)
{
driver_remove_file(driver, &driver_attr_version);
}
// 示例2:
static ssize_t debug_show(struct device_driver *dev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", debug_path);
}
static ssize_t debug_store(struct device_driver *dev, const char *buf,
size_t count)
{
strncpy(debug_path, buf, PATH_MAX);
return count;
}
static DRIVER_ATTR_RW(debug);
static int sample2_create_driver_attrs(struct device_driver *driver)
{
return driver_create_file(driver, &driver_attr_debug);
}
static void sample2_remove_driver_attrs(struct device_driver *driver)
{
driver_remove_file(driver, &driver_attr_debug);
}
//sysfs.txt
结构体:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf,
size_t count);
};
声明:
DRIVER_ATTR(_name, _mode, _show, _store)
增/删属性:
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
八:i2c driver
#include <linux/i2c.h>
#define SAMPLE_DRIVER_I2C "sample_devive_name"
/* Return negative errno else zero on success */
static int sample_write_byte(struct i2c_client *client, u8 addr, u8 val)
{
int rval;
rval = i2c_smbus_write_byte_data(client, addr, val);
if (rval < 0) {
LOG_ERROR("i2c write error, err %d\n", rval);
return rval;
}
LOG_DEBUG("write addr:0x%02x val:0x%02x ok\n", addr, val);
return 0;
}
/* Return negative errno else a data byte received from the device. */
static int sample_read_byte(struct i2c_client *client, u8 addr)
{
int rval;
rval = i2c_smbus_read_byte_data(client, addr);
if (rval < 0) {
LOG_ERROR("i2c read error, err %d\n", rval);
return rval;
}
LOG_DEBUG("read addr:0x%02x val:0x%02x ok\n", addr, rval);
return rval;
}
static int32_t sample_driver_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct customer_data *cust_data = NULL;
struct device *dev;
struct device_node *np;
int rc;
dev = &client->dev;
np = client->dev.of_node;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
LOG_ERROR( "%s :: i2c_check_functionality failed",
client->name);
rc = -EFAULT;
return rc;
}
// alloc customer struct data
cust_data = kzalloc(sizeof(*customer_struct_data), GFP_KERNEL);
if (!cust_data)
return -ENOMEM;
i2c_set_clientdata(client, cust_data);
// TODO: parse dt
// TODO: init power, gpio, clk, irq
// TODO: register subsystem driver
return rc;
free_ctrl:
kfree(cust_data);
return rc;
}
static int32_t sample_driver_i2c_remove(struct i2c_client *client)
{
struct customer_data *cust_data =
i2c_get_clientdata(client);
if (!cust_data) {
LOG_ERROR("cust_data is NULL");
return -EINVAL;
}
// TODO : shutdown device
// TODO : unreister subsystem driver
// TODO : release resource
// TODO: Free Allocated Mem
kfree(cust_data);
return 0;
}
static const struct of_device_id sample_driver_dt_match[] = {
{.compatible = "vendor,device_name"},
{}
};
MODULE_DEVICE_TABLE(of, sample_driver_dt_match);
/* not use id_table match, use of_match_table */
//static const struct i2c_device_id i2c_id[] = {
// {SAMPLE_DRIVER_I2C, (kernel_ulong_t)NULL},
// { }
//};
static struct i2c_driver sample_driver_i2c = {
// .id_table = i2c_id,
.probe = sample_driver_i2c_probe,
.remove = sample_driver_i2c_remove,
.driver = {
.name = SAMPLE_DRIVER_I2C,
.of_match_table = sample_driver_dt_match,
},
};
module_i2c_driver(sample_driver_i2c);
MODULE_DESCRIPTION("sample_driver");
MODULE_LICENSE("GPL v2");
九:platform driver
#include <linux/platform_device.h>
static int32_t sample_driver_platform_probe(
struct platform_device *pdev)
{
struct customer_data *cust_data = NULL;
struct device *dev;
struct device_node *np;
int rc;
dev = &pdev->dev;
np = pdev->dev.of_node;
LOG_FUNC_ENTER();
// TODO: parse dt
// TODO: init power, gpio, clk, irq
// TODO: register subsystem driver
// TODO: create driver attr, debufs
return rc;
}
static int32_t sample_platform_remove(
struct platform_device *pdev)
{
LOG_FUNC_ENTER();
// TODO : shutdown device
// TODO : unreister subsystem driver
// TODO : release resource
// TODO: Free Allocated Mem
return 0;
}
static const struct of_device_id sample_driver_dt_match[] = {
{.compatible = "vendor,device_name"},
{}
};
MODULE_DEVICE_TABLE(of, sample_driver_dt_match);
struct platform_driver sample_platform_driver = {
.probe = sample_driver_platform_probe,
.driver = {
.name = "qcom,actuator",
.owner = THIS_MODULE,
.of_match_table = sample_driver_dt_match,
.suppress_bind_attrs = true,
},
.remove = sample_platform_remove,
};
static int __init sample_driver_init(void)
{
int32_t rc = 0;
rc = platform_driver_register(&sample_platform_driver);
if (rc < 0) {
LOG_ERROR(
"platform_driver_register failed rc = %d", rc);
return rc;
}
return rc;
}
module_init(sample_driver_init);
static void __exit sample_driver_exit(void)
{
platform_driver_unregister(&sample_platform_driver);
}
module_exit(sample_driver_exit);
MODULE_DESCRIPTION("sample_driver");
MODULE_LICENSE("GPL v2");
十:log
#define LOG_TAG "[sample_driver]"
#define LOG_DEBUG_EN (1)
#if LOG_DEBUG_EN
#define LOG_DEBUG(fmt, args...) do { \
printk(LOG_TAG "D %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)
#define LOG_FUNC_ENTER() do { \
printk(LOG_TAG "D %s line %d: Enter\n", __func__, __LINE__); \
} while (0)
#define LOG_FUNC_EXIT() do { \
printk(LOG_TAG "D %s line %d: Exit\n", __func__, __LINE__); \
} while (0)
#else /* #if LOG_DEBUG_EN*/
#define LOG_DEBUG(fmt, args...)
#define LOG_FUNC_ENTER()
#define LOG_FUNC_EXIT()
#endif
#define LOG_INFO(fmt, args...) do { \
printk(KERN_INFO LOG_TAG "I %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)
#define LOG_ERROR(fmt, args...) do { \
printk(KERN_ERR LOG_TAG "E %s line %d:"fmt"\n", __func__, __LINE__, ##args); \
} while (0)
十一:完成量
#include <linux/completion.h>
//struct completion preisp_wp_comp;
//init_completion(&preisp_wp_comp);
static DECLARE_COMPLETION(preisp_wp_comp);
wait_for_completion(&preisp_wp_comp);
//if (wait_for_completion_timeout(&preisp_wp_comp, msecs_to_jiffies(2000)) == 0)
// pr_err("%s: timeout! fail wake up preisp\n", __func__);
complete(&preisp_wp_comp);