linux 驱动 --- 不使用设备树注册I2C设备
不使用设备树注册I2C设备,主要使用以下API:
i2c_get_adapter() 获取指定I2C适配器
i2c_new_device() 在指定 I2C 适配器下创建一个I2C设备,通过
i2c_board_info 结构体提供设备的静态信息(如设备地址、名称、平台数据等),并基于这些信息创建 i2c_client 实例。在支持设备树的系统中,通常优先使用设备树描述设备,内核会自动解析并调用 i2c_new_device 完成注册。注:这种方式还是需要设备树描述适配器,完成适配器的初始化。
实例:
#define pr_fmt(fmt) "[Et6312 driver]" fmt #include <linux/types.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/i2c.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/device.h> #include <linux/timer.h> #include <linux/ioctl.h> #include <linux/gpio.h> #include "et6312.h" // data struct for userspace =========================================== #if defined(CONFIG_REMO_PDT_TP_TINY2) #define REMO_RGB_LED_NUM 3 #else #define REMO_RGB_LED_NUM 4 #endif #define RGB_VALUE_R(__rgb) ((__rgb) & 0xFF) #define RGB_VALUE_G(__rgb) ((__rgb >> 8) & 0xFF) #define RGB_VALUE_B(__rgb) ((__rgb >> 16) & 0xFF) #define MAKE_RGB_VALUE(__r, __g, __b) ((__r & 0xFF) | ((__g & 0xFF) << 8) | ((__b & 0xFF) << 16)) #if REMO_RGB_LED_NUM == 3 #define LED_COLORSET_SAME_COLORS(__rgb) {__rgb, __rgb, __rgb} #elif REMO_RGB_LED_NUM == 4 #define LED_COLORSET_SAME_COLORS(__rgb) {__rgb, __rgb, __rgb, __rgb} #endif #pragma pack(1) struct remo_led_color_set { u32 rgb_colors[REMO_RGB_LED_NUM]; }; struct remo_led_marquee_set { u32 rgb_colors[REMO_RGB_LED_NUM]; u16 delay_ms; // delay time for each light }; struct remo_led_breathing_set { u32 rgb_colors[REMO_RGB_LED_NUM]; u16 period_ms; }; struct remo_led_blink_set { u32 rgb_colors[REMO_RGB_LED_NUM]; u16 interval_ms; }; struct remo_led_queue_set { u32 rgb_colors[REMO_RGB_LED_NUM]; u16 delay_ms; }; #pragma pack() #define REMO_LED_SET_COLOR _IOW(0x81, 0x14, struct remo_led_color_set) #define REMO_LED_SET_MARQUEE _IOW(0x81, 0x15, struct remo_led_marquee_set) #define REMO_LED_SET_BREATHING _IOW(0x81, 0x16, struct remo_led_breathing_set) #define REMO_LED_SET_BLINK _IOW(0x81, 0x17, struct remo_led_blink_set) #define REMO_LED_SET_QUEUE _IOW(0x81, 0x18, struct remo_led_queue_set) #define REMO_LED_SHUT_DOWN _IO(0x81, 0x30) #define REMO_LED_RESET _IO(0x81, 0x31) #define REMO_LED_GET_STATUS _IOR(0x81, 0x32, int) // ===================================================================== #define ET6312_I2C_ADDRESS 0x48 #define ET6312_I2C_BUS_ID 5 // et6312 has two current range, 24mA or 48mA, we choose 24mA #define ET6312_CHAN_MAX_CURRENT 0xFF // 24mA #define ET6312_CHAN_DEFAULT_CURRENT 0x70 // 14mA #define ET6312_USED_CHANNEL_NUM (3 * REMO_RGB_LED_NUM) #define ET6312_RISE_FALL_SCALE 2 enum et6312_led_workmode { LED_WORK_MODE_ALWAYS_OFF, LED_WORK_MODE_ALWAYS_ON, LED_WORK_MODE_THREAD_1, LED_WORK_MODE_THREAD_2, LED_WORK_MODE_THREAD_3, }; enum et6312_clk_io_mode { CLK_IO_MODE_INPUT, CLK_IO_MODE_OUTPUT_0, CLK_IO_MODE_OUTPUT_INNER_CLOCK, }; enum et6312_max_current_mode { MAX_CURRENT_MODE_24MA, MAX_CURRENT_MODE_48MA }; enum et6312_timer_mode { TIMER_MODE_1, TIMER_MODE_2, TIMER_MODE_3, TIMER_MODE_4, }; enum et6312_rise_fall_scale_type { RISE_FALL_SCALE_1X_NORMAL, RISE_FALL_SCALE_2X_SLOWER, RISE_FALL_SCALE_4X_SLOWER, RISE_FALL_SCALE_8X_FASTER }; struct et6312_config_ops; #pragma pack(1) struct et6312_device { bool is_connected; u8 max_channel; struct i2c_client *client; struct et6312_config_ops *cfg_ops; struct mutex lock; }; struct et6312_devices_data { struct i2c_adapter *bus_adapter; struct et6312_device device; struct miscdevice miscdev; }; // flash/blink config struct et6312_flash_config { u8 period; u8 timer1_on; u8 timer2_on; u8 trise : 4; u8 tfall : 4; u8 start_delay; }; #pragma pack() struct et6312_config_ops { int (*init_device) (struct et6312_device *device); int (*get_status) (struct et6312_device *device); int (*set_auto_flash_enable) (struct et6312_device *device, bool is_enable); int (*set_clk_io_mode) (struct et6312_device *device, enum et6312_clk_io_mode mode); int (*set_shutdown_enable) (struct et6312_device *device, bool is_enable); int (*set_scl_shutdown_enable) (struct et6312_device *device, bool is_enable); int (*set_max_current_mode) (struct et6312_device *device, enum et6312_max_current_mode mode); int (*set_timer_mode) (struct et6312_device *device, enum et6312_timer_mode); int (*set_flash_config) (struct et6312_device *device, u8 rgb_n, struct et6312_flash_config *flash_config); int (*set_rf_scale) (struct et6312_device *device, u8 rgb_n, enum et6312_rise_fall_scale_type scale_type); int (*set_led_work_mode) (struct et6312_device *device, u8 channel, enum et6312_led_workmode mode); int (*set_led_current) (struct et6312_device *device, u8 channel, u8 val); }; static int skip_led = 1; static int auto_deinit = 1; module_param(skip_led, int, 0644); module_param(auto_deinit, int, 0644); static struct i2c_board_info dev_info [] = { { // I2C_BOARD_INFO("ET6312_M", (BOARD_MASTER_I2C_ADDER)), I2C_BOARD_INFO("ET6312", ET6312_I2C_ADDRESS), }, }; static int et6312_i2c_write(const struct i2c_client *client, u8 reg, u8 val) { return i2c_smbus_write_byte_data(client, reg, val); } static int et6312_i2c_read(const struct i2c_client *client, u8 reg, u8 *buf, u8 len) { int ret, i; struct i2c_msg msg[2]; u8 tranfer_buf[0xFF]; if ((len == 0) || (buf == NULL)){ return -1; } tranfer_buf[0] = reg; msg[0].addr = client->addr; msg[0].flags = client->flags & I2C_M_TEN; msg[0].len = 1; msg[0].buf = tranfer_buf; msg[1].addr = client->addr; msg[1].flags = client->flags & I2C_M_TEN; msg[1].flags |= I2C_M_RD; msg[1].len = len; msg[1].buf = tranfer_buf; ret = i2c_transfer(client->adapter, msg, 2); if(ret == 2) { for(i = 0; i < len; i++){ buf[i] = tranfer_buf[i]; } return 0; } return -1; } static int et6312_i2c_write_bytes(const struct i2c_client *client, u8 reg, u8 *buf, u8 len) { u8 tranfer_buf[0xFF]; int ret, i; if ((len == 0) || (buf == NULL)){ return -EINVAL; } tranfer_buf[0] = reg; // printk("write to 0x%02x data: ", reg); for(i = 0; i < len; i ++){ tranfer_buf[i + 1] = buf[i]; // printk("%02x ", buf[i]); } // printk("\n"); ret = i2c_master_send(client, tranfer_buf, len + 1); if (ret != (len + 1)) { return -1; } return 0; } static int et6312_i2c_init(struct et6312_devices_data *data) { int i; data->bus_adapter = i2c_get_adapter(ET6312_I2C_BUS_ID); if (NULL == data->bus_adapter) { pr_info("get i2c new adapter error.\n"); return -1; } data->device.client = i2c_new_device(data->bus_adapter, &dev_info[0]); // data->device.client = i2c_new_device(data->bus_adapter, &et6312_i2c_dev_info); if (NULL == data->device.client) { data->device.is_connected = false; pr_info("get i2c new device for %d chip error.\n", i); } else { data->device.is_connected = true; } return 0; } static void et6312_i2c_release(struct et6312_devices_data *data) { if (data->device.client != NULL) { i2c_unregister_device(data->device.client); } } static int et6312_init_device (struct et6312_device *device) { int ret; u8 i = 0; mutex_init(&(device->lock)); // val |= CLKDIR; // val |= TIMER_MODE_1; // ret = et6312_i2c_write(device->client, REG_CHIPCTR, val); // if(ret){ // chip->is_connected = false; // return -EIO; // } // == uboot init chip, don't let channel down if (0) { for (i = 0; i < device->max_channel; ++i) { ret = device->cfg_ops->set_led_work_mode(device, i, LED_WORK_MODE_ALWAYS_OFF); if (ret) { return -EIO; } } } ret = device->cfg_ops->set_shutdown_enable(device, false); if (ret) { return -EIO; } ret = device->cfg_ops->set_scl_shutdown_enable(device, false); if (ret) { return -EIO; } return ret; } static int et6312_set_auto_flash_enable (struct et6312_device *device, bool is_enable) { int ret = 0; u8 val = 0; ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } if (is_enable) { val |= AUTO_BRECON; } else { val &= ~AUTO_BRECON; } return et6312_i2c_write(device->client, REG_CHIPCTR, val); } static int et6312_set_clk_io_mode (struct et6312_device *device, enum et6312_clk_io_mode mode) { int ret = 0; u8 val = 0; u8 clk_io_mode_val_table[3] = {0x01, 0x00, 0x02}; if (mode > CLK_IO_MODE_OUTPUT_INNER_CLOCK) { return -EINVAL; } ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } val &= ~(0x03 << 5); // clear bit5:6 val |= clk_io_mode_val_table[mode]; return et6312_i2c_write(device->client, REG_CHIPCTR, val); } static int et6312_get_status (struct et6312_device *device) { int ret = 0; u8 val = 0; ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return 0; } if (0 == (val & SOFTDN_EN)) { return 0; } return 1; } // if REG_CHIPCTR bit 4 is 1, chip go to work, else chip in standby mode static int et6312_set_shutdown_enable (struct et6312_device *device, bool is_enable) { int ret = 0; u8 val = 0; ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } if (is_enable) { val &= ~SOFTDN_EN; } else { val |= SOFTDN_EN; } return et6312_i2c_write(device->client, REG_CHIPCTR, val); } static int et6312_set_scl_shutdown_enable (struct et6312_device *device, bool is_enable) { int ret = 0; u8 val = 0; ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } if (is_enable) { val |= PDEN; } else { val &= ~PDEN; } return et6312_i2c_write(device->client, REG_CHIPCTR, val); } static int et6312_set_max_current_mode (struct et6312_device *device, enum et6312_max_current_mode mode) { int ret = 0; u8 val = 0; ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } if (mode == MAX_CURRENT_MODE_24MA) { val &= ~IMAX_SEL; } else { val |= IMAX_SEL; } return et6312_i2c_write(device->client, REG_CHIPCTR, val); } static int et6312_set_timer_mode (struct et6312_device *device, enum et6312_timer_mode mode) { int ret; u8 val; if (mode > TIMER_MODE_4) { return -EINVAL; } ret = et6312_i2c_read(device->client, REG_CHIPCTR, &val, 1); if (ret) { return -EIO; } val &= ~TMD_SEL; val |= (mode & TMD_SEL); ret = et6312_i2c_write(device->client, REG_CHIPCTR, val); if (ret) { return -EIO; } return 0; } static int et6312_set_flash_config (struct et6312_device *device, u8 rgb_n, struct et6312_flash_config *flash_config) { int ret; if (rgb_n >= REMO_RGB_LED_NUM) { return -EINVAL; } ret = et6312_i2c_write_bytes(device->client, REG_FLASH_PERIOD_RGB0 + (rgb_n * 5), (u8 *)(flash_config), sizeof(struct et6312_flash_config)); return ret; } static int et6312_set_rf_scale (struct et6312_device *device, u8 rgb_n, enum et6312_rise_fall_scale_type scale_type) { int ret; u8 val = 0; if (rgb_n >= REMO_RGB_LED_NUM) { return -EINVAL; } ret = et6312_i2c_read(device->client, REG_RF_SCALE, &val, 1); if (ret) { return -EIO; } val &= ~(0x03 << (rgb_n * 2)); val |= ((scale_type & 0x03) << (rgb_n * 2)); ret = et6312_i2c_write(device->client, REG_RF_SCALE, val); return ret; } static u8 et6312_led_logical_to_physical(u8 logical_led) { #if defined(CONFIG_REMO_PDT_TP_TINY2) u8 led_group_map[REMO_RGB_LED_NUM] = {0, 1, 2}; #else u8 led_group_map[REMO_RGB_LED_NUM] = {3, 0, 1, 2}; #endif if (logical_led > REMO_RGB_LED_NUM) { return 0; } return led_group_map[logical_led]; } static int et6312_set_led_work_mode (struct et6312_device *device, u8 channel, enum et6312_led_workmode mode) { int ret = 0; u8 addr = (channel / 3) + REG_LEDXMD_RGB0; u8 bit = channel % 3; u8 val = 0; u8 mode_magic_val_table[3] = {0x03, 0x1C, 0xE0}; u8 mode_magic_bit_table[3] ={0, 2, 5}; // printk("channel %u write mode %u\n", channel, mode); if (channel >= device->max_channel || mode > LED_WORK_MODE_THREAD_3) { return -EINVAL; } ret = et6312_i2c_read(device->client, addr, &val, 1); if (ret) { return -EIO; } if (bit == 0 && mode == LED_WORK_MODE_THREAD_3) { // led 1/4/7/10 only can carry on thread1/2 mode = LED_WORK_MODE_THREAD_1; } val &= ~(mode_magic_val_table[bit]); val |= ((mode << mode_magic_bit_table[bit]) & mode_magic_val_table[bit]); // printk("channel %u write mode addr 0x%02x with %u\n", channel, addr, val); ret = et6312_i2c_write(device->client, addr, val); if (ret) { return -EIO; } return 0; } static int et6312_set_led_current (struct et6312_device *device, u8 channel, u8 val) { u8 addr; if (channel >= device->max_channel) { return -EINVAL; } addr = channel + REG_LED1_CURT; // printk("set current to addr 0x%02x with %d\n", addr, val); return et6312_i2c_write(device->client, addr, val); } static int et6312_set_led_color(struct et6312_device *device, struct remo_led_color_set *color_set) { // int i, ret = 0, j = 0; // int channel; int ret = 0; u8 i, j, channel; u8 rgb_val[3]; device->cfg_ops->set_auto_flash_enable(device, false); for (i = 0; i < REMO_RGB_LED_NUM; ++i) { rgb_val[0] = RGB_VALUE_R(color_set->rgb_colors[i]); rgb_val[1] = RGB_VALUE_G(color_set->rgb_colors[i]); rgb_val[2] = RGB_VALUE_B(color_set->rgb_colors[i]); for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (rgb_val[j] > 0) { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_ON); ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]); } else { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } } } return ret; } static int et6312_set_led_color_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { // int i, ret = 0, j = 0; // int channel; struct et6312_device *device; struct remo_led_color_set color_set; if(copy_from_user(&color_set, (struct remo_led_color_set __user *)arg, sizeof(color_set))){ return -EFAULT; } device = &pdata->device; return et6312_set_led_color(device, &color_set); } // static int et6312_set_led_marquee(struct et6312_device *device, struct remo_led_marquee_set *marquee_set) { int ret = 0; u8 i, j, channel; u8 rgb_val[3]; struct et6312_flash_config blink_config = {0}; device->cfg_ops->set_timer_mode(device, TIMER_MODE_1); ret |= device->cfg_ops->set_auto_flash_enable(device, false); blink_config.period = (REMO_RGB_LED_NUM - 1) * marquee_set->delay_ms / 128; blink_config.timer1_on = 256 / 2; for (i = 0; i < REMO_RGB_LED_NUM; ++i) { rgb_val[0] = RGB_VALUE_R(marquee_set->rgb_colors[i]); rgb_val[1] = RGB_VALUE_G(marquee_set->rgb_colors[i]); rgb_val[2] = RGB_VALUE_B(marquee_set->rgb_colors[i]); for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (rgb_val[j] > 0) { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1); ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]); } else { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } } blink_config.start_delay = i * marquee_set->delay_ms / (128 * ET6312_RISE_FALL_SCALE); ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER); ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config); } ret |= device->cfg_ops->set_auto_flash_enable(device, true); return ret; } static int et6312_set_led_marquee_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { struct et6312_device *device; struct remo_led_marquee_set marquee_set; if (copy_from_user(&marquee_set, (struct remo_led_marquee_set __user *)arg, sizeof(marquee_set))) { return -EFAULT; } device = &pdata->device; return et6312_set_led_marquee(device, &marquee_set); } static int et6312_set_led_breathing(struct et6312_device *device, struct remo_led_breathing_set *breathing_set) { int ret = 0; u8 i, j, channel; u8 rgb_val[3]; struct et6312_flash_config blink_config = {0}; device->cfg_ops->set_timer_mode(device, TIMER_MODE_1); ret |= device->cfg_ops->set_auto_flash_enable(device, false); blink_config.period = breathing_set->period_ms / 128; blink_config.timer1_on = 125; blink_config.trise = breathing_set->period_ms / (128 * ET6312_RISE_FALL_SCALE); blink_config.tfall = breathing_set->period_ms / (128 * ET6312_RISE_FALL_SCALE); // blink_config.tfall = 15; for (i = 0; i < REMO_RGB_LED_NUM; ++i) { rgb_val[0] = RGB_VALUE_R(breathing_set->rgb_colors[i]); rgb_val[1] = RGB_VALUE_G(breathing_set->rgb_colors[i]); rgb_val[2] = RGB_VALUE_B(breathing_set->rgb_colors[i]); for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (rgb_val[j] > 0) { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1); ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]); } else { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } } blink_config.start_delay = 0; ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER); ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config); } ret |= device->cfg_ops->set_auto_flash_enable(device, true); return ret; } static int et6312_set_led_breathing_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { struct et6312_device *device; struct remo_led_breathing_set breathing_set; if (copy_from_user(&breathing_set, (struct remo_led_breathing_set __user *)arg, sizeof(breathing_set))) { return -EFAULT; } device = &pdata->device; return et6312_set_led_breathing(device, &breathing_set); } static int et6312_set_led_blink(struct et6312_device *device, struct remo_led_blink_set *blink_set) { int ret = 0; u8 i, j, channel; u8 rgb_val[3]; struct et6312_flash_config blink_config = {0}; device->cfg_ops->set_timer_mode(device, TIMER_MODE_1); ret |= device->cfg_ops->set_auto_flash_enable(device, false); blink_config.period = blink_set->interval_ms / 128; blink_config.timer1_on = 125; // blink_config.tfall = 15; for (i = 0; i < REMO_RGB_LED_NUM; ++i) { rgb_val[0] = RGB_VALUE_R(blink_set->rgb_colors[i]); rgb_val[1] = RGB_VALUE_G(blink_set->rgb_colors[i]); rgb_val[2] = RGB_VALUE_B(blink_set->rgb_colors[i]); for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (rgb_val[j] > 0) { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1); ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]); } else { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } } blink_config.start_delay = 0; ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER); ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config); } ret |= device->cfg_ops->set_auto_flash_enable(device, true); return ret; } static int et6312_set_led_blink_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { struct et6312_device *device; struct remo_led_blink_set blink_set; if (copy_from_user(&blink_set, (struct remo_led_blink_set __user *)arg, sizeof(blink_set))) { return -EFAULT; } device = &pdata->device; return et6312_set_led_blink(device, &blink_set); } static int et6312_set_led_queue(struct et6312_device *device, struct remo_led_queue_set *queue_set) { int ret = 0; u8 i, j, channel; u8 rgb_val[3]; struct et6312_flash_config blink_config = {0}; device->cfg_ops->set_timer_mode(device, TIMER_MODE_1); ret |= device->cfg_ops->set_auto_flash_enable(device, false); blink_config.period = 5 * queue_set->delay_ms / (128); for (i = 0; i < REMO_RGB_LED_NUM; ++i) { rgb_val[0] = RGB_VALUE_R(queue_set->rgb_colors[i]); rgb_val[1] = RGB_VALUE_G(queue_set->rgb_colors[i]); rgb_val[2] = RGB_VALUE_B(queue_set->rgb_colors[i]); for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (rgb_val[j] > 0) { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1); ret |= device->cfg_ops->set_led_current(device, channel, rgb_val[j]); } else { device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } } blink_config.timer1_on = (REMO_RGB_LED_NUM - i) * 256 / 5; blink_config.start_delay = (i + 1) * queue_set->delay_ms / (128); ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER); ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config); } ret |= device->cfg_ops->set_auto_flash_enable(device, true); return ret; } static int et6312_set_led_queue_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { struct et6312_device *device; struct remo_led_queue_set queue_set; if (copy_from_user(&queue_set, (struct remo_led_queue_set __user *)arg, sizeof(queue_set))) { return -EFAULT; } device = &pdata->device; return et6312_set_led_queue(device, &queue_set); } static int et6312_shutdown_ioctl(struct et6312_devices_data *pdata) { struct et6312_device *device; device = &pdata->device; return device->cfg_ops->set_shutdown_enable(device, true); } static int et6312_reset_ioctl(struct et6312_devices_data *pdata) { struct et6312_device *device; device = &pdata->device; return device->cfg_ops->set_shutdown_enable(device, false); } static int et6312_get_status_ioctl(struct et6312_devices_data *pdata, unsigned long arg) { int status = 0; struct et6312_device *device; device = &pdata->device; status = device->cfg_ops->get_status(device); if (copy_to_user((int __user *)arg, &status, sizeof(int))) { return -EFAULT; } return 0; } static int et6312_set_default_blue_marquee(struct et6312_device *device) { struct remo_led_marquee_set marquee_set = { #if defined(CONFIG_REMO_PDT_TP_TINY2) .rgb_colors = LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 5, 50)), #else .rgb_colors = LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 30, 180)), #endif .delay_ms = 800, }; return et6312_set_led_marquee(device, &marquee_set); } // green and blue blink 500ms static int et6312_set_upgrading_mode(struct et6312_device *device) { int ret = 0; u8 i, j, channel; struct et6312_flash_config blink_config = {0}; device->cfg_ops->set_timer_mode(device, TIMER_MODE_1); ret |= device->cfg_ops->set_auto_flash_enable(device, false); blink_config.period = 2000 / 256; blink_config.timer1_on = 255 / REMO_RGB_LED_NUM; blink_config.timer2_on = 255 / REMO_RGB_LED_NUM; /* #define LED_COLORSET_GREEN_ALL LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 0x25, 0)) #define LED_COLORSET_BLUE_ALL LED_COLORSET_SAME_COLORS(MAKE_RGB_VALUE(0, 0, 0x25)) */ for (i = 0; i < REMO_RGB_LED_NUM; ++i) { for (j = 0; j < 3; ++j) { channel = i * 3 + j; if (j == 0) { //r device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } else if (j == 1) { //g device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_1); ret |= device->cfg_ops->set_led_current(device, channel, 0x25); // device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_ALWAYS_OFF); } else { //b device->cfg_ops->set_led_work_mode(device, channel, LED_WORK_MODE_THREAD_3); ret |= device->cfg_ops->set_led_current(device, channel, 0x25); } } blink_config.start_delay = 0; ret |= device->cfg_ops->set_rf_scale(device, et6312_led_logical_to_physical(i), RISE_FALL_SCALE_2X_SLOWER); ret |= device->cfg_ops->set_flash_config(device, et6312_led_logical_to_physical(i), &blink_config); } ret |= device->cfg_ops->set_auto_flash_enable(device, true); return ret; } static long et6312_ioctl_ops(struct file *file, unsigned int cmd, unsigned long arg) { int ret; struct et6312_devices_data *pdata = container_of(file->private_data, struct et6312_devices_data, miscdev); switch(cmd) { case REMO_LED_SET_COLOR: ret = et6312_set_led_color_ioctl(pdata, arg); break; case REMO_LED_SET_MARQUEE: ret = et6312_set_led_marquee_ioctl(pdata, arg); break; case REMO_LED_SET_BREATHING: ret = et6312_set_led_breathing_ioctl(pdata, arg); break; case REMO_LED_SET_BLINK: ret = et6312_set_led_blink_ioctl(pdata, arg); break; case REMO_LED_SET_QUEUE: ret = et6312_set_led_queue_ioctl(pdata, arg); break; case REMO_LED_SHUT_DOWN: ret = et6312_shutdown_ioctl(pdata); break; case REMO_LED_RESET: ret = et6312_reset_ioctl(pdata); break; case REMO_LED_GET_STATUS: ret = et6312_get_status_ioctl(pdata, arg); break; default: return ENOTTY; } return ret; } static int et6312_open_ops(struct inode *inode, struct file *filp) { struct et6312_devices_data *pdata = container_of(filp->private_data, struct et6312_devices_data, miscdev); if (!pdata->device.is_connected) { return -EIO; } return 0; } static int et6312_release_ops(struct inode *inode, struct file *filp) { return 0; } static struct et6312_config_ops et6312_cfg_ops = { .init_device = et6312_init_device, .get_status = et6312_get_status, .set_auto_flash_enable = et6312_set_auto_flash_enable, .set_clk_io_mode = et6312_set_clk_io_mode, .set_shutdown_enable = et6312_set_shutdown_enable, .set_scl_shutdown_enable = et6312_set_scl_shutdown_enable, .set_max_current_mode = et6312_set_max_current_mode, .set_timer_mode = et6312_set_timer_mode, .set_flash_config = et6312_set_flash_config, .set_rf_scale = et6312_set_rf_scale, .set_led_work_mode = et6312_set_led_work_mode, .set_led_current = et6312_set_led_current, }; static struct file_operations et6312_fops = { .owner = THIS_MODULE, .unlocked_ioctl = et6312_ioctl_ops, .open = et6312_open_ops, .release = et6312_release_ops, }; static struct et6312_devices_data et6312_data; static const char *device_name = "remo_led"; int et6312_module_init(void) { int ret = 0; et6312_data.device.max_channel = ET6312_USED_CHANNEL_NUM; et6312_data.bus_adapter = NULL; et6312_data.miscdev.minor = MISC_DYNAMIC_MINOR; et6312_data.miscdev.name = device_name; et6312_data.miscdev.fops = &et6312_fops; ret = et6312_i2c_init(&et6312_data); if (ret) { pr_info("Init et6312 i2c client fail %d\n", ret); goto i2c_error; } et6312_data.device.cfg_ops = &et6312_cfg_ops; ret = et6312_data.device.cfg_ops->init_device(&et6312_data.device); if (ret) { pr_info("init_device faild with %d\n", ret); et6312_data.device.is_connected = false; goto i2c_error; } else { et6312_data.device.is_connected = true; } // == init this state in uboot if (!skip_led) { et6312_set_default_blue_marquee(&(et6312_data.device)); } // == init this state in uboot if (0) { et6312_set_upgrading_mode(&(et6312_data.device)); } ret = misc_register(&(et6312_data.miscdev)); if (ret) { pr_info("register et6312 misc device fail.%d\n", ret); goto register_error; } // device_create_file(et6312_data.miscdev.this_device, &dev_attr_led_enable); // device_create_file(et6312_data.miscdev.this_device, &dev_attr_led_duty); return 0; register_error: misc_deregister(&et6312_data.miscdev); i2c_error: et6312_i2c_release(&et6312_data); return -1; } void et6312_module_exit(void) { pr_info("ET6312 module exit.\n"); // == don't disable it by software, just let it power off if (auto_deinit) { et6312_data.device.cfg_ops->set_shutdown_enable(&et6312_data.device, true); } et6312_i2c_release(&et6312_data); misc_deregister(&et6312_data.miscdev); } module_init(et6312_module_init); module_exit(et6312_module_exit); MODULE_AUTHOR("remo"); MODULE_DESCRIPTION("et6312 driver"); MODULE_LICENSE("GPL");

浙公网安备 33010602011771号