公司的车载项目用到了一颗艾为的led芯片驱动16路led,下面描述一下调试过程
硬件原理图如下
aw9523 驱动采用led驱动框架,比较简单,调用内核API直接注册进led子系统可以了,下面是dts配置
i2c3: i2c@e400b000 {
aw9523@5B{
compatible = "awinic,aw9523_led";
reg = <0x5B>;
aw9523_reset-gpios = <&gpio 36 0>;
aw9523,gps_led_green {
aw9523,name = "gps_green";
aw9523,id = <0>;
aw9523,max-brightness = <255>;
};
aw9523,gps_led_red {
aw9523,name = "gps_red";
aw9523,id = <1>;
aw9523,max-brightness = <255>;
};
aw9523,wifi_led_green {
aw9523,name = "wifi_green";
aw9523,id = <2>;
aw9523,max-brightness = <255>;
};
aw9523,wifi_led_red {
aw9523,name = "wifi_red";
aw9523,id = <3>;
aw9523,max-brightness = <255>;
};
aw9523,notification_led_blue {
aw9523,name = "noti_blue";
aw9523,id = <4>;
aw9523,max-brightness = <255>;
};
aw9523,notification_led_green {
aw9523,name = "noti_green";
aw9523,id = <5>;
aw9523,max-brightness = <255>;
};i2c3: i2c@e400b000 {
aw9523,notification_led_red {
aw9523,name = "noti_red";
aw9523,id = <6>;
aw9523,max-brightness = <255>;
};
aw9523,eld_led_blue {
aw9523,name = "eld_blue";
aw9523,id = <7>;
aw9523,max-brightness = <255>;
};
aw9523,eld_led_green {
aw9523,name = "eld_green";
aw9523,id = <8>;
aw9523,max-brightness = <255>;
};
aw9523,eld_led_red {
aw9523,name = "eld_red";
aw9523,id = <9>;
aw9523,max-brightness = <255>;
};
aw9523,bt_led_green {
aw9523,name = "bt_green";
aw9523,id = <12>;
aw9523,max-brightness = <255>;
};
aw9523,bt_led_red {
aw9523,name = "bt_red";
aw9523,id = <13>;
aw9523,max-brightness = <255>;
};
aw9523,cell_led_green {
aw9523,name = "cell_green";
aw9523,id = <14>;
aw9523,max-brightness = <255>;
};
aw9523,cell_led_red {
aw9523,name = "cell_red";
aw9523,id = <15>;
aw9523,max-brightness = <255>;
};
};
下面是驱动源码
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* Version: v1.0.0
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/leds-aw9523.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
/* aw9523 register */
#define AW9523_REG_CHIPID 0x10
#define AW9523_LED_MODE_P0 0x12
#define AW9523_LED_MODE_P1 0x13
#define AW9523_LED_MODE 0x00
#define AW9523_CHIPID 0x23
#define AW9523_REG_MAX 0x7F
#define AW9523_REG_BRIGHTNESS_BASE 0x20
struct aw9523_led {
struct i2c_client *client;
struct led_classdev cdev;
struct work_struct brightness_work;
struct aw9523_platform_data *pdata;
struct mutex lock;
int num_leds;
int id;
int reset_gpio;
};
static int aw9523_write(struct aw9523_led *led, u8 reg, u8 val)
{
int ret = -EINVAL, retry_times = 0;
do {
ret = i2c_smbus_write_byte_data(led->client, reg, val);
retry_times ++;
if(retry_times == 5)
break;
}while (ret < 0);
return ret;
}
static int aw9523_read(struct aw9523_led *led, u8 reg, u8 *val)
{
int ret = -EINVAL, retry_times = 0;
do{
ret = i2c_smbus_read_byte_data(led->client, reg);
retry_times ++;
if(retry_times == 5)
break;
}while (ret < 0);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static void aw9523_brightness_work(struct work_struct *work)
{
struct aw9523_led *led = container_of(work, struct aw9523_led,
brightness_work);
mutex_lock(&led->pdata->led->lock);
dev_err(&led->client->dev,
"%s:enter, id = %d name = %s, brightness = %d\n",__func__,led->id,led->cdev.name,led->cdev.brightness);
if (led->cdev.brightness >= 0) {
if (led->cdev.brightness > led->cdev.max_brightness)
led->cdev.brightness = led->cdev.max_brightness;
aw9523_write(led, AW9523_REG_BRIGHTNESS_BASE + led->id, led->cdev.brightness);
}else{
dev_err(&led->client->dev,
"brightness is fault, set brightness fail\n");
}
mutex_unlock(&led->pdata->led->lock);
}
static void aw9523_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct aw9523_led *led = container_of(cdev, struct aw9523_led, cdev);
led->cdev.brightness = brightness;
schedule_work(&led->brightness_work);
}
static ssize_t aw9523_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct aw9523_led *led =
container_of(led_cdev, struct aw9523_led, cdev);
unsigned char i, reg_val;
ssize_t len = 0;
for(i=0; i<AW9523_REG_MAX; i++) {
aw9523_read(led, i, ®_val);
len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n", i, reg_val);
}
return len;
}
static ssize_t aw9523_reg_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct aw9523_led *led =
container_of(led_cdev, struct aw9523_led, cdev);
unsigned int databuf[2];
if(2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1]))
{
aw9523_write(led, (unsigned char)databuf[0], (unsigned char)databuf[1]);
}
return len;
}
static DEVICE_ATTR(reg, 0664, aw9523_reg_show, aw9523_reg_store);
static struct attribute *aw9523_led_attributes[] = {
&dev_attr_reg.attr,
NULL,
};
static struct attribute_group aw9523_led_attr_group = {
.attrs = aw9523_led_attributes
};
static int aw9523_check_chipid(struct aw9523_led *led)
{
u8 val;
u8 cnt;
for(cnt = 5; cnt > 0; cnt --)
{
aw9523_read(led, AW9523_REG_CHIPID, &val);
dev_err(&led->client->dev,"aw9523 chip id %0x",val);
if (val == AW9523_CHIPID)
return 0;
}
return -EINVAL;
}
static int aw9523_led_err_handle(struct aw9523_led *led_array,
int parsed_leds)
{
int i;
/*
* If probe fails, cannot free resource of all LEDs, only free
* resources of LEDs which have allocated these resource really.
*/
for (i = 0; i < parsed_leds; i++) {
sysfs_remove_group(&led_array[i].cdev.dev->kobj,
&aw9523_led_attr_group);
led_classdev_unregister(&led_array[i].cdev);
cancel_work_sync(&led_array[i].brightness_work);
devm_kfree(&led_array->client->dev, led_array[i].pdata);
led_array[i].pdata = NULL;
}
return i;
}
static void switch_to_led_mode(struct aw9523_led *led)
{
aw9523_write(led, AW9523_LED_MODE_P0, AW9523_LED_MODE);
aw9523_write(led, AW9523_LED_MODE_P1, AW9523_LED_MODE);
}
static int aw9523_led_parse_child_node(struct aw9523_led *led_array,
struct device_node *node)
{
struct aw9523_led *led;
struct device_node *temp;
struct aw9523_platform_data *pdata;
int rc = 0, parsed_leds = 0;
for_each_child_of_node(node, temp) {
led = &led_array[parsed_leds];
led->client = led_array->client;
pdata = devm_kzalloc(&led->client->dev,
sizeof(struct aw9523_platform_data),
GFP_KERNEL);
if (!pdata) {
dev_err(&led->client->dev,
"Failed to allocate memory\n");
goto free_err;
}
pdata->led = led_array;
led->pdata = pdata;
rc = of_property_read_string(temp, "aw9523,name",
&led->cdev.name);
if (rc < 0) {
dev_err(&led->client->dev,
"Failure reading led name, rc = %d\n", rc);
goto free_pdata;
}
rc = of_property_read_u32(temp, "aw9523,id",
&led->id);
if (rc < 0) {
dev_err(&led->client->dev,
"Failure reading id, rc = %d\n", rc);
goto free_pdata;
}
rc = of_property_read_u32(temp, "aw9523,max-brightness",
&led->cdev.max_brightness);
if (rc < 0) {
dev_err(&led->client->dev,
"Failure reading max-brightness, rc = %d\n",
rc);
goto free_pdata;
}
INIT_WORK(&led->brightness_work, aw9523_brightness_work);
led->cdev.brightness_set = aw9523_set_brightness;
//注册进led子系统
rc = led_classdev_register(&led->client->dev, &led->cdev);
if (rc) {
dev_err(&led->client->dev,
"unable to register led %d,rc=%d\n",
led->id, rc);
goto free_pdata;
}
rc = sysfs_create_group(&led->cdev.dev->kobj,
&aw9523_led_attr_group);
if (rc) {
dev_err(&led->client->dev, "led sysfs rc: %d\n", rc);
goto free_class;
}
parsed_leds++;
}
return 0;
free_class:
aw9523_led_err_handle(led_array, parsed_leds);
led_classdev_unregister(&led_array[parsed_leds].cdev);
cancel_work_sync(&led_array[parsed_leds].brightness_work);
devm_kfree(&led->client->dev, led_array[parsed_leds].pdata);
led_array[parsed_leds].pdata = NULL;
return rc;
free_pdata:
aw9523_led_err_handle(led_array, parsed_leds);
devm_kfree(&led->client->dev, led_array[parsed_leds].pdata);
return rc;
free_err:
aw9523_led_err_handle(led_array, parsed_leds);
return rc;
}
static int aw9523_led_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct aw9523_led *led_array;
struct device_node *node;
int ret, num_leds = 0;
enum of_gpio_flags flags;
node = client->dev.of_node;
if (node == NULL)
return -EINVAL;
num_leds = of_get_child_count(node);
if (!num_leds)
return -EINVAL;
led_array = devm_kzalloc(&client->dev,
(sizeof(struct aw9523_led) * num_leds), GFP_KERNEL);
if (!led_array)
return -ENOMEM;
led_array->client = client;
led_array->num_leds = num_leds;
mutex_init(&led_array->lock);
//解析dts中内容
ret = aw9523_led_parse_child_node(led_array, node);
if (ret) {
dev_err(&client->dev, "parsed node error\n");
goto free_led_arry;
}
led_array->reset_gpio = of_get_named_gpio_flags(node, "aw9523_reset-gpios", 0, &flags);
if (gpio_is_valid(led_array->reset_gpio)) {
ret = devm_gpio_request(&client->dev, led_array->reset_gpio, "aw9523_led_reset");
if (ret < 0){
dev_err(&client->dev, "Failed to request reset_pin: %d\n", ret);
return ret;
}
gpio_direction_output(led_array->reset_gpio,1);
msleep(20);
gpio_direction_output(led_array->reset_gpio,0);
msleep(20);
gpio_direction_output(led_array->reset_gpio,1);
msleep(20);
dev_err(&client->dev, "get reset gpio sucess,%d\n",led_array->reset_gpio);
}else {
dev_err(&client->dev, "get reset gpio fail,%d\n",led_array->reset_gpio);
}
i2c_set_clientdata(client, led_array);
ret = aw9523_check_chipid(led_array);
if (ret) {
dev_err(&client->dev, "Check chip id error\n");
goto fail_parsed_node;
}
//aw9523有两种模式,gpio和led模式,切换到led模式
switch_to_led_mode(led_array);
dev_err(&client->dev, "aw9523_led_probe sucess\n");
return 0;
fail_parsed_node:
aw9523_led_err_handle(led_array, num_leds);
free_led_arry:
mutex_destroy(&led_array->lock);
devm_kfree(&client->dev, led_array);
led_array = NULL;
return ret;
}
static int aw9523_led_remove(struct i2c_client *client)
{
struct aw9523_led *led_array = i2c_get_clientdata(client);
int i, parsed_leds = led_array->num_leds;
for (i = 0; i < parsed_leds; i++) {
sysfs_remove_group(&led_array[i].cdev.dev->kobj,
&aw9523_led_attr_group);
led_classdev_unregister(&led_array[i].cdev);
cancel_work_sync(&led_array[i].brightness_work);
devm_kfree(&client->dev, led_array[i].pdata);
led_array[i].pdata = NULL;
}
mutex_destroy(&led_array->lock);
devm_kfree(&client->dev, led_array);
led_array = NULL;
return 0;
}
static const struct i2c_device_id aw9523_led_id[] = {
{"aw9523_led", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, aw9523_led_id);
static struct of_device_id aw9523_match_table[] = {
{ .compatible = "awinic,aw9523_led",},
{ },
};
static struct i2c_driver aw9523_led_driver = {
.probe = aw9523_led_probe,
.remove = aw9523_led_remove,
.driver = {
.name = "aw9523_led",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(aw9523_match_table),
},
.id_table = aw9523_led_id,
};
static int __init aw9523_led_init(void)
{
return i2c_add_driver(&aw9523_led_driver);
}
module_init(aw9523_led_init);
static void __exit aw9523_led_exit(void)
{
i2c_del_driver(&aw9523_led_driver);
}
module_exit(aw9523_led_exit);
MODULE_AUTHOR("<chengbingbing");
MODULE_DESCRIPTION("AWINIC AW9523 LED driver");
MODULE_LICENSE("GPL v2");
leds-aw9523.h中内容
#ifndef __LINUX_AW9523_LED_H__
#define __LINUX_AW9523_LED_H__
/* The definition of each time described as shown in figure.
* /-----------\
* / | \
* /| | |\
* / | | | \-----------
* |hold_time_ms | |
* | | |
* rise_time_ms fall_time_ms |
* off_time_ms
*/
struct aw9523_platform_data {
int imax;
int led_current;
int rise_time_ms;
int hold_time_ms;
int fall_time_ms;
int off_time_ms;
struct aw9523_led *led;
};
#endif
~