1 leds驱动
新增文件:arch\arm\boot\dts\overlays\example-overlay.dts
/dts-v1/;
/plugin/;
/{
compatible = "brcm,bcm2835";
fragment@0 {
target = <&gpio>;
__overlay__ {
leds_pins: leds_pins {
brcm,pins = <25>;
brcm,function = <1>; /* out */
};
};
};
fragment@1 {
target = <&i2c_arm>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
aw9523b: aw9523b@5b {
compatible = "xxx,aw9523b-leds";
reg = <0x5b>;
pinctrl-names = "default";
pinctrl-0 = <&leds_pins>;
reset-gpios = <&gpio 25 0>;
status = "okay";
};
};
};
__overrides__ {
aw9523b = <&aw9523b>,"status";
};
};
linux\arch\arm\boot\dts\overlays\Makefile
dtbo-$(RPI_DT_OVERLAYS) += example-leds.dtbo
linux\drivers\leds\leds-aw9523b.c
/*
* TI LP8860 4-Channel LED Driver
*
* Copyright (C) 2014 Texas Instruments
*
* Author: Dan Murphy <dmurphy@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/delay.h>
#define AW9523B_REG_CHIPID 0x10
#define AW9523B_CHIPID_VALUE 0x23
#define AW9523B_LED_OFFSET 3
#define AW9523B_COLOR_OFFSET 3
static struct mutex aw9523b_mutex;
enum led_ids {
LED1,
LED2,
LED3,
LED_NUM,
};
enum led_colors {
LED1_RED = 0,
LED1_GREEN = 1,
LED1_BLUE = 2,
LED2_RED = 3,
LED2_GREEN = 4,
LED2_BLUE = 5,
LED3_RED = 6,
LED3_GREEN = 7,
LED3_BLUE = 8,
};
struct aw9523b_led {
struct aw9523b_led_platform_data *pdata;
struct i2c_client *client;
struct rw_semaphore rwsem;
struct work_struct work;
/*
* Making led_classdev as array is not recommended, because array
* members prevent using 'container_of' macro. So repetitive works
* are needed.
*/
struct led_classdev cdev_led1r;
struct led_classdev cdev_led1g;
struct led_classdev cdev_led1b;
struct led_classdev cdev_led2r;
struct led_classdev cdev_led2g;
struct led_classdev cdev_led2b;
struct led_classdev cdev_led3r;
struct led_classdev cdev_led3g;
struct led_classdev cdev_led3b;
enum led_ids led_id;
enum led_colors color;
enum led_brightness brightness;
int reset_gpio;
};
static inline u8 aw9523b_get_base_offset(enum led_ids id, enum led_colors color)
{
return id * AW9523B_LED_OFFSET + color;
}
static inline u8 aw9523b_get_reg_addr(enum led_ids id, enum led_colors color,
u8 reg_offset)
{
return reg_offset + aw9523b_get_base_offset(id, color);
}
/*--------------------------------------------------------------*/
/* AW9523BGU core functions */
/*--------------------------------------------------------------*/
static int aw9523b_write_byte(struct i2c_client *client, u8 reg, u8 val)
{
int ret;
mutex_lock(&aw9523b_mutex);
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret >= 0)
{
mutex_unlock(&aw9523b_mutex);
return 0;
}
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, reg, val, ret);
mutex_unlock(&aw9523b_mutex);
return ret;
}
static int aw9523b_read_byte(struct i2c_client *client, u8 reg, u8 *val)
{
int ret;
mutex_lock(&aw9523b_mutex);
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
{
mutex_unlock(&aw9523b_mutex);
return -EIO;
}
*val = ret;
mutex_unlock(&aw9523b_mutex);
return 0;
}
static void aw9523b_reset(struct i2c_client *client)
{
struct aw9523b_led *led = i2c_get_clientdata(client);
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
gpio_direction_output(led->reset_gpio, 0);
/* Tacss = min 0.1ms */
udelay(20);
gpio_direction_output(led->reset_gpio, 1);
udelay(100);
aw9523b_write_byte(client,0x12,0x00); //OUT4~9配置为呼吸灯模式
aw9523b_write_byte(client,0x12,0x00); //OUT4~9配置为呼吸灯模式
aw9523b_write_byte(client,0x13,0x00); //OUT0~3配置为呼吸灯模式
aw9523b_write_byte(client,0x13,0x00); //OUT0~3配置为呼吸灯模式
}
static void aw9523b_set_brightness(struct aw9523b_led *led, enum led_ids id,
enum led_colors color, enum led_brightness brightness)
{
u8 reg;
int ret;
reg = aw9523b_get_reg_addr(id, color, 0x20);
ret = aw9523b_write_byte(led->client, reg, brightness);
if(ret <0)
aw9523b_reset(led->client);
}
#define AW9523B_SET_REGISTER(reg_addr, reg_name) \
static ssize_t aw9523b_store_reg##reg_addr(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct aw9523b_led *led = i2c_get_clientdata(to_i2c_client(dev));\
unsigned long val; \
int ret; \
if (!count) \
return -EINVAL; \
ret = kstrtoul(buf, 16, &val); \
if (ret) \
return ret; \
down_write(&led->rwsem); \
aw9523b_write_byte(led->client, reg_addr, (u8) val); \
up_write(&led->rwsem); \
return count; \
} \
static struct device_attribute aw9523b_reg##reg_addr##_attr = { \
.attr = {.name = reg_name, .mode = 0644}, \
.store = aw9523b_store_reg##reg_addr, \
};
AW9523B_SET_REGISTER(0x20, "0x20");
AW9523B_SET_REGISTER(0x21, "0x21");
AW9523B_SET_REGISTER(0x22, "0x22");
AW9523B_SET_REGISTER(0x23, "0x23");
AW9523B_SET_REGISTER(0x24, "0x24");
AW9523B_SET_REGISTER(0x25, "0x25");
AW9523B_SET_REGISTER(0x26, "0x26");
AW9523B_SET_REGISTER(0x27, "0x27");
AW9523B_SET_REGISTER(0x28, "0x28");
AW9523B_SET_REGISTER(0x29, "0x29");
AW9523B_SET_REGISTER(0x2a, "0x2a");
AW9523B_SET_REGISTER(0x2b, "0x2b");
AW9523B_SET_REGISTER(0x2c, "0x2c");
AW9523B_SET_REGISTER(0x2d, "0x2d");
AW9523B_SET_REGISTER(0x2e, "0x2e");
AW9523B_SET_REGISTER(0x2f, "0x2f");
static struct device_attribute *aw9523b_addr_attributes[] = {
&aw9523b_reg0x20_attr,
&aw9523b_reg0x21_attr,
&aw9523b_reg0x22_attr,
&aw9523b_reg0x23_attr,
&aw9523b_reg0x24_attr,
&aw9523b_reg0x25_attr,
&aw9523b_reg0x26_attr,
&aw9523b_reg0x27_attr,
&aw9523b_reg0x28_attr,
&aw9523b_reg0x29_attr,
&aw9523b_reg0x2a_attr,
&aw9523b_reg0x2b_attr,
&aw9523b_reg0x2c_attr,
&aw9523b_reg0x2d_attr,
&aw9523b_reg0x2e_attr,
&aw9523b_reg0x2f_attr,
};
static void aw9523b_enable_adv_conf(struct aw9523b_led *led)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(aw9523b_addr_attributes); i++) {
ret = device_create_file(&led->client->dev,
aw9523b_addr_attributes[i]);
if (ret) {
dev_err(&led->client->dev, "failed: sysfs file %s\n",
aw9523b_addr_attributes[i]->attr.name);
goto failed_remove_files;
}
}
return;
failed_remove_files:
for (i--; i >= 0; i--)
device_remove_file(&led->client->dev,
aw9523b_addr_attributes[i]);
}
static void aw9523b_disable_adv_conf(struct aw9523b_led *led)
{
int i;
for (i = 0; i < ARRAY_SIZE(aw9523b_addr_attributes); i++)
device_remove_file(&led->client->dev,
aw9523b_addr_attributes[i]);
}
static void aw9523b_led_work(struct work_struct *work)
{
struct aw9523b_led *led = container_of(work, struct aw9523b_led, work);
//printk("led%d: color = %d,brightness = %d\n",led->led_id, led->color, led->brightness);
aw9523b_set_brightness(led, led->led_id, led->color, led->brightness);
}
#define AW9523B_CONTROL_RGBS(name, id, clr) \
static void aw9523b_set_##name##_brightness(struct led_classdev *led_cdev,\
enum led_brightness value) \
{ \
struct aw9523b_led *led = \
container_of(led_cdev, struct aw9523b_led, cdev_##name); \
led->led_id = id; \
led->color = clr; \
led->brightness = value; \
schedule_work(&led->work); \
}
AW9523B_CONTROL_RGBS(led1r, LED1, LED1_RED);
AW9523B_CONTROL_RGBS(led1g, LED1, LED1_GREEN);
AW9523B_CONTROL_RGBS(led1b, LED1, LED1_BLUE);
AW9523B_CONTROL_RGBS(led2r, LED2, LED2_RED);
AW9523B_CONTROL_RGBS(led2g, LED2, LED2_GREEN);
AW9523B_CONTROL_RGBS(led2b, LED2, LED2_BLUE);
AW9523B_CONTROL_RGBS(led3r, LED3, LED3_RED);
AW9523B_CONTROL_RGBS(led3g, LED3, LED3_GREEN);
AW9523B_CONTROL_RGBS(led3b, LED3, LED3_BLUE);
static int aw9523b_register_led_classdev(struct aw9523b_led *led)
{
int ret;
INIT_WORK(&led->work, aw9523b_led_work);
//led1//
led->cdev_led1r.name = "led1_R";
led->cdev_led1r.brightness = LED_OFF;
led->cdev_led1r.brightness_set = aw9523b_set_led1r_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1r.name);
goto failed_unregister_led1_R;
}
led->cdev_led1g.name = "led1_G";
led->cdev_led1g.brightness = LED_OFF;
led->cdev_led1g.brightness_set = aw9523b_set_led1g_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1g.name);
goto failed_unregister_led1_G;
}
led->cdev_led1b.name = "led1_B";
led->cdev_led1b.brightness = LED_OFF;
led->cdev_led1b.brightness_set = aw9523b_set_led1b_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1b.name);
goto failed_unregister_led1_B;
}
//led2//
led->cdev_led2r.name = "led2_R";
led->cdev_led2r.brightness = LED_OFF;
led->cdev_led2r.brightness_set = aw9523b_set_led2r_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2r.name);
goto failed_unregister_led2_R;
}
led->cdev_led2g.name = "led2_G";
led->cdev_led2g.brightness = LED_OFF;
led->cdev_led2g.brightness_set = aw9523b_set_led2g_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2g.name);
goto failed_unregister_led2_G;
}
led->cdev_led2b.name = "led2_B";
led->cdev_led2b.brightness = LED_OFF;
led->cdev_led2b.brightness_set = aw9523b_set_led2b_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2b.name);
goto failed_unregister_led2_B;
}
//led3//
led->cdev_led3r.name = "led3_R";
led->cdev_led3r.brightness = LED_OFF;
led->cdev_led3r.brightness_set = aw9523b_set_led3r_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led3r);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led3r.name);
goto failed_unregister_led3_R;
}
led->cdev_led3g.name = "led3_G";
led->cdev_led3g.brightness = LED_OFF;
led->cdev_led3g.brightness_set = aw9523b_set_led3g_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led3g);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led3g.name);
goto failed_unregister_led3_G;
}
led->cdev_led3b.name = "led3_B";
led->cdev_led3b.brightness = LED_OFF;
led->cdev_led3b.brightness_set = aw9523b_set_led3b_brightness;
ret = led_classdev_register(&led->client->dev, &led->cdev_led3b);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led3b.name);
goto failed_unregister_led3_B;
}
return 0;
failed_unregister_led3_B:
led_classdev_unregister(&led->cdev_led3g);
failed_unregister_led3_G:
led_classdev_unregister(&led->cdev_led3r);
failed_unregister_led3_R:
led_classdev_unregister(&led->cdev_led2b);
failed_unregister_led2_B:
led_classdev_unregister(&led->cdev_led2g);
failed_unregister_led2_G:
led_classdev_unregister(&led->cdev_led2r);
failed_unregister_led2_R:
led_classdev_unregister(&led->cdev_led1b);
failed_unregister_led1_B:
led_classdev_unregister(&led->cdev_led1g);
failed_unregister_led1_G:
led_classdev_unregister(&led->cdev_led1r);
failed_unregister_led1_R:
return ret;
}
static void aw9523b_unregister_led_classdev(struct aw9523b_led *led)
{
cancel_work_sync(&led->work);
led_classdev_unregister(&led->cdev_led1r);
led_classdev_unregister(&led->cdev_led1g);
led_classdev_unregister(&led->cdev_led1b);
led_classdev_unregister(&led->cdev_led2r);
led_classdev_unregister(&led->cdev_led2g);
led_classdev_unregister(&led->cdev_led2b);
led_classdev_unregister(&led->cdev_led3r);
led_classdev_unregister(&led->cdev_led3g);
led_classdev_unregister(&led->cdev_led3b);
}
int aw9523b_match_id(struct i2c_client *client)
{
u8 id = 0;
int tries = 5;
while(tries > 0)
{
aw9523b_read_byte(client,AW9523B_REG_CHIPID,&id);
printk("read led control chip id: 0x%x\n", id);
if (id == AW9523B_CHIPID_VALUE)
{
printk("matched");
return 0;
}
tries--;
}
return 1;
}
static int aw9523b_init(struct aw9523b_led *led)
{
int ret = 0;
struct i2c_client *client = led->client;
if (led->reset_gpio)
{
gpio_set_value(led->reset_gpio, 0);
/* Tacss = min 0.1ms */
udelay(20);
gpio_set_value(led->reset_gpio, 1);
udelay(100);
}
ret = aw9523b_match_id(client);
if(ret) {
dev_err(&led->client->dev, "Cannot read chip\n");
goto out;
}
aw9523b_write_byte(client,0x12,0x00); //OUT4~9配置为呼吸灯模式
aw9523b_write_byte(client,0x12,0x00); //OUT4~9配置为呼吸灯模式
aw9523b_write_byte(client,0x13,0x00); //OUT0~3配置为呼吸灯模式
aw9523b_write_byte(client,0x13,0x00); //OUT0~3配置为呼吸灯模式
return 0;
out:
if (ret)
if (led->reset_gpio)
gpio_set_value(led->reset_gpio, 0);
return ret;
}
static int aw9523b_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct aw9523b_led *led;
int gpio = 0;
printk("%s:enter\n",__FUNCTION__);
mutex_init(&aw9523b_mutex);
led = kzalloc(sizeof(struct aw9523b_led), GFP_KERNEL);
if (!led) {
dev_err(&client->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
if (client->dev.of_node) {
/* Get GPIO from device tree */
gpio = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
if (gpio < 0) {
dev_err(&client->dev,
"Failed to retrieve reset-gpios from device tree\n");
return gpio;
}
/* GPIO request and configuration */
ret = devm_gpio_request_one(&client->dev, gpio,
GPIOF_OUT_INIT_HIGH, "aw9523b_reset");
if (ret) {
dev_err(&client->dev, "Failed to request reset pin\n");
goto failed_free;
}
}
led->reset_gpio = gpio;
led->client = client;
init_rwsem(&led->rwsem);
i2c_set_clientdata(client, led);
ret = aw9523b_init(led);
if (ret)
goto failed_free;
ret = aw9523b_register_led_classdev(led);
if (ret) {
dev_err(&client->dev, "led register err: %d\n", ret);
goto failed_free;
}
//aw9523b_enable_adv_conf(led);
printk("%s:exit\n",__FUNCTION__);
return 0;
failed_free:
kfree(led);
return ret;
}
static int aw9523b_remove(struct i2c_client *client)
{
struct aw9523b_led *led = i2c_get_clientdata(client);
gpio_set_value(led->reset_gpio, 0);
aw9523b_unregister_led_classdev(led);
//aw9523b_disable_adv_conf(led);
kfree(led);
return 0;
}
static const struct i2c_device_id aw9523b_id[] = {
{ "aw9523b", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw9523b_id);
#ifdef CONFIG_OF
static const struct of_device_id of_aw9523b_leds_match[] = {
{ .compatible = "xxx,aw9523b-leds", },
{},
};
MODULE_DEVICE_TABLE(of, of_aw9523b_leds_match);
#endif
static struct i2c_driver aw9523b_driver = {
.driver = {
.name = "aw9523b",
.of_match_table = of_match_ptr(of_aw9523b_leds_match),
},
.probe = aw9523b_probe,
.remove = aw9523b_remove,
.id_table = aw9523b_id,
};
module_i2c_driver(aw9523b_driver);
MODULE_DESCRIPTION("AW9523B LED driver");
MODULE_AUTHOR("Thornton Wu <447116230@qq.com>");
MODULE_LICENSE("GPL");
linux\drivers\leds\Makefile
obj-m += leds-aw9523b.o
2 KERNEL编译
export PATH=$PATH:/home/thornton/work/pi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/home/thornton/work/pi/firmware modules_install
3 KERNEL更新
将RPI T卡插在LINUX系统下,会弹出两个盘,一个是KERENL盘和一个BOOT盘
将firmware/lib拷到KERNEL盘,其他拷到BOOT盘
4 启动驱动
/boot/config.txt
dtoverlay=example-leds
/etc/ modules
leds-aw9523b
/etc/udev/rules.d/ 99-com.rules
SUBSYSTEM=="leds", PROGRAM="/bin/sh -c '\
chmod 666 /sys/class/leds/led1_R/brightness;\
chmod 666 /sys/class/leds/led1_G/brightness;\
chmod 666 /sys/class/leds/led1_B/brightness;\
chmod 666 /sys/class/leds/led2_R/brightness;\
chmod 666 /sys/class/leds/led2_G/brightness;\
chmod 666 /sys/class/leds/led2_B/brightness;\
chmod 666 /sys/class/leds/led3_R/brightness;\
chmod 666 /sys/class/leds/led3_G/brightness;\
chmod 666 /sys/class/leds/led3_B/brightness;\
'"