Commit 29fd0ec2 authored by Nick Dyer's avatar Nick Dyer Committed by Dmitry Torokhov

Input: synaptics-rmi4 - add support for F34 device reflash

Add support for updating firmware, triggered by a sysfs attribute.

This patch has been tested on Synaptics S7300.
Signed-off-by: default avatarNick Dyer <nick@shmanahar.org>
Tested-by: default avatarChris Healy <cphealy@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 792f497b
...@@ -74,6 +74,17 @@ config RMI4_F30 ...@@ -74,6 +74,17 @@ config RMI4_F30
Function 30 provides GPIO and LED support for RMI4 devices. This Function 30 provides GPIO and LED support for RMI4 devices. This
includes support for buttons on TouchPads and ClickPads. includes support for buttons on TouchPads and ClickPads.
config RMI4_F34
bool "RMI4 Function 34 (Device reflash)"
depends on RMI4_CORE
select FW_LOADER
help
Say Y here if you want to add support for RMI4 function 34.
Function 34 provides support for upgrading the firmware on the RMI4
device via the firmware loader interface. This is triggered using a
sysfs attribute.
config RMI4_F54 config RMI4_F54
bool "RMI4 Function 54 (Analog diagnostics)" bool "RMI4 Function 54 (Analog diagnostics)"
depends on RMI4_CORE depends on RMI4_CORE
......
...@@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o ...@@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o
rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
# Transports # Transports
......
...@@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = { ...@@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = {
#ifdef CONFIG_RMI4_F30 #ifdef CONFIG_RMI4_F30
&rmi_f30_handler, &rmi_f30_handler,
#endif #endif
#ifdef CONFIG_RMI4_F34
&rmi_f34_handler,
#endif
#ifdef CONFIG_RMI4_F54 #ifdef CONFIG_RMI4_F54
&rmi_f54_handler, &rmi_f54_handler,
#endif #endif
......
...@@ -35,14 +35,24 @@ ...@@ -35,14 +35,24 @@
#define RMI_DEVICE_RESET_CMD 0x01 #define RMI_DEVICE_RESET_CMD 0x01
#define DEFAULT_RESET_DELAY_MS 100 #define DEFAULT_RESET_DELAY_MS 100
static void rmi_free_function_list(struct rmi_device *rmi_dev) void rmi_free_function_list(struct rmi_device *rmi_dev)
{ {
struct rmi_function *fn, *tmp; struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n"); rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n");
mutex_lock(&data->irq_mutex);
devm_kfree(&rmi_dev->dev, data->irq_memory);
data->irq_memory = NULL;
data->irq_status = NULL;
data->fn_irq_bits = NULL;
data->current_irq_mask = NULL;
data->new_irq_mask = NULL;
data->f01_container = NULL; data->f01_container = NULL;
data->f34_container = NULL;
/* Doing it in the reverse order so F01 will be removed last */ /* Doing it in the reverse order so F01 will be removed last */
list_for_each_entry_safe_reverse(fn, tmp, list_for_each_entry_safe_reverse(fn, tmp,
...@@ -50,7 +60,10 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev) ...@@ -50,7 +60,10 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev)
list_del(&fn->node); list_del(&fn->node);
rmi_unregister_function(fn); rmi_unregister_function(fn);
} }
mutex_unlock(&data->irq_mutex);
} }
EXPORT_SYMBOL_GPL(rmi_free_function_list);
static int reset_one_function(struct rmi_function *fn) static int reset_one_function(struct rmi_function *fn)
{ {
...@@ -147,24 +160,25 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) ...@@ -147,24 +160,25 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
if (!data) if (!data)
return 0; return 0;
mutex_lock(&data->irq_mutex);
if (!data->irq_status || !data->f01_container) {
mutex_unlock(&data->irq_mutex);
return 0;
}
if (!rmi_dev->xport->attn_data) { if (!rmi_dev->xport->attn_data) {
error = rmi_read_block(rmi_dev, error = rmi_read_block(rmi_dev,
data->f01_container->fd.data_base_addr + 1, data->f01_container->fd.data_base_addr + 1,
data->irq_status, data->num_of_irq_regs); data->irq_status, data->num_of_irq_regs);
if (error < 0) { if (error < 0) {
dev_err(dev, "Failed to read irqs, code=%d\n", error); dev_err(dev, "Failed to read irqs, code=%d\n", error);
mutex_unlock(&data->irq_mutex);
return error; return error;
} }
} }
mutex_lock(&data->irq_mutex);
bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask,
data->irq_count); data->irq_count);
/*
* At this point, irq_status has all bits that are set in the
* interrupt status register and are enabled.
*/
mutex_unlock(&data->irq_mutex);
/* /*
* It would be nice to be able to use irq_chip to handle these * It would be nice to be able to use irq_chip to handle these
...@@ -180,6 +194,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) ...@@ -180,6 +194,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
if (data->input) if (data->input)
input_sync(data->input); input_sync(data->input);
mutex_unlock(&data->irq_mutex);
return 0; return 0;
} }
...@@ -244,11 +260,17 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev) ...@@ -244,11 +260,17 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev)
struct rmi_function *entry; struct rmi_function *entry;
int retval; int retval;
mutex_lock(&data->irq_mutex);
list_for_each_entry(entry, &data->function_list, node) { list_for_each_entry(entry, &data->function_list, node) {
retval = suspend_one_function(entry); retval = suspend_one_function(entry);
if (retval < 0) if (retval < 0) {
mutex_unlock(&data->irq_mutex);
return retval; return retval;
} }
}
mutex_unlock(&data->irq_mutex);
return 0; return 0;
} }
...@@ -278,16 +300,22 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev) ...@@ -278,16 +300,22 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev)
struct rmi_function *entry; struct rmi_function *entry;
int retval; int retval;
mutex_lock(&data->irq_mutex);
list_for_each_entry(entry, &data->function_list, node) { list_for_each_entry(entry, &data->function_list, node) {
retval = resume_one_function(entry); retval = resume_one_function(entry);
if (retval < 0) if (retval < 0) {
mutex_unlock(&data->irq_mutex);
return retval; return retval;
} }
}
mutex_unlock(&data->irq_mutex);
return 0; return 0;
} }
static int enable_sensor(struct rmi_device *rmi_dev) int rmi_enable_sensor(struct rmi_device *rmi_dev)
{ {
int retval = 0; int retval = 0;
...@@ -297,6 +325,7 @@ static int enable_sensor(struct rmi_device *rmi_dev) ...@@ -297,6 +325,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
return rmi_process_interrupt_requests(rmi_dev); return rmi_process_interrupt_requests(rmi_dev);
} }
EXPORT_SYMBOL_GPL(rmi_enable_sensor);
/** /**
* rmi_driver_set_input_params - set input device id and other data. * rmi_driver_set_input_params - set input device id and other data.
...@@ -502,10 +531,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, ...@@ -502,10 +531,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
RMI_SCAN_DONE : RMI_SCAN_CONTINUE; RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
} }
static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
int (*callback)(struct rmi_device *rmi_dev, int (*callback)(struct rmi_device *rmi_dev,
void *ctx, void *ctx, const struct pdt_entry *entry))
const struct pdt_entry *entry))
{ {
int page; int page;
int empty_pages = 0; int empty_pages = 0;
...@@ -520,6 +548,7 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, ...@@ -520,6 +548,7 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
return retval < 0 ? retval : 0; return retval < 0 ? retval : 0;
} }
EXPORT_SYMBOL_GPL(rmi_scan_pdt);
int rmi_read_register_desc(struct rmi_device *d, u16 addr, int rmi_read_register_desc(struct rmi_device *d, u16 addr,
struct rmi_register_descriptor *rdesc) struct rmi_register_descriptor *rdesc)
...@@ -740,19 +769,15 @@ static int rmi_count_irqs(struct rmi_device *rmi_dev, ...@@ -740,19 +769,15 @@ static int rmi_count_irqs(struct rmi_device *rmi_dev,
int *irq_count = ctx; int *irq_count = ctx;
*irq_count += pdt->interrupt_source_count; *irq_count += pdt->interrupt_source_count;
if (pdt->function_number == 0x01) { if (pdt->function_number == 0x01)
data->f01_bootloader_mode = data->f01_bootloader_mode =
rmi_check_bootloader_mode(rmi_dev, pdt); rmi_check_bootloader_mode(rmi_dev, pdt);
if (data->f01_bootloader_mode)
dev_warn(&rmi_dev->dev,
"WARNING: RMI4 device is in bootloader mode!\n");
}
return RMI_SCAN_CONTINUE; return RMI_SCAN_CONTINUE;
} }
static int rmi_initial_reset(struct rmi_device *rmi_dev, int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
void *ctx, const struct pdt_entry *pdt) const struct pdt_entry *pdt)
{ {
int error; int error;
...@@ -787,6 +812,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev, ...@@ -787,6 +812,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
/* F01 should always be on page 0. If we don't find it there, fail. */ /* F01 should always be on page 0. If we don't find it there, fail. */
return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV;
} }
EXPORT_SYMBOL_GPL(rmi_initial_reset);
static int rmi_create_function(struct rmi_device *rmi_dev, static int rmi_create_function(struct rmi_device *rmi_dev,
void *ctx, const struct pdt_entry *pdt) void *ctx, const struct pdt_entry *pdt)
...@@ -828,6 +854,8 @@ static int rmi_create_function(struct rmi_device *rmi_dev, ...@@ -828,6 +854,8 @@ static int rmi_create_function(struct rmi_device *rmi_dev,
if (pdt->function_number == 0x01) if (pdt->function_number == 0x01)
data->f01_container = fn; data->f01_container = fn;
else if (pdt->function_number == 0x34)
data->f34_container = fn;
list_add_tail(&fn->node, &data->function_list); list_add_tail(&fn->node, &data->function_list);
...@@ -893,6 +921,7 @@ static int rmi_driver_remove(struct device *dev) ...@@ -893,6 +921,7 @@ static int rmi_driver_remove(struct device *dev)
disable_irq(irq); disable_irq(irq);
rmi_f34_remove_sysfs(rmi_dev);
rmi_free_function_list(rmi_dev); rmi_free_function_list(rmi_dev);
return 0; return 0;
...@@ -919,13 +948,12 @@ static inline int rmi_driver_of_probe(struct device *dev, ...@@ -919,13 +948,12 @@ static inline int rmi_driver_of_probe(struct device *dev,
} }
#endif #endif
static int rmi_probe_interrupts(struct rmi_driver_data *data) int rmi_probe_interrupts(struct rmi_driver_data *data)
{ {
struct rmi_device *rmi_dev = data->rmi_dev; struct rmi_device *rmi_dev = data->rmi_dev;
struct device *dev = &rmi_dev->dev; struct device *dev = &rmi_dev->dev;
int irq_count; int irq_count;
size_t size; size_t size;
void *irq_memory;
int retval; int retval;
/* /*
...@@ -941,31 +969,38 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data) ...@@ -941,31 +969,38 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data)
dev_err(dev, "IRQ counting failed with code %d.\n", retval); dev_err(dev, "IRQ counting failed with code %d.\n", retval);
return retval; return retval;
} }
if (data->f01_bootloader_mode)
dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n");
data->irq_count = irq_count; data->irq_count = irq_count;
data->num_of_irq_regs = (data->irq_count + 7) / 8; data->num_of_irq_regs = (data->irq_count + 7) / 8;
size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL);
if (!irq_memory) { if (!data->irq_memory) {
dev_err(dev, "Failed to allocate memory for irq masks.\n"); dev_err(dev, "Failed to allocate memory for irq masks.\n");
return retval; return retval;
} }
data->irq_status = irq_memory + size * 0; data->irq_status = data->irq_memory + size * 0;
data->fn_irq_bits = irq_memory + size * 1; data->fn_irq_bits = data->irq_memory + size * 1;
data->current_irq_mask = irq_memory + size * 2; data->current_irq_mask = data->irq_memory + size * 2;
data->new_irq_mask = irq_memory + size * 3; data->new_irq_mask = data->irq_memory + size * 3;
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(rmi_probe_interrupts);
static int rmi_init_functions(struct rmi_driver_data *data) int rmi_init_functions(struct rmi_driver_data *data)
{ {
struct rmi_device *rmi_dev = data->rmi_dev; struct rmi_device *rmi_dev = data->rmi_dev;
struct device *dev = &rmi_dev->dev; struct device *dev = &rmi_dev->dev;
int irq_count; int irq_count;
int retval; int retval;
mutex_lock(&data->irq_mutex);
irq_count = 0; irq_count = 0;
rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__);
retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
...@@ -990,12 +1025,16 @@ static int rmi_init_functions(struct rmi_driver_data *data) ...@@ -990,12 +1025,16 @@ static int rmi_init_functions(struct rmi_driver_data *data)
goto err_destroy_functions; goto err_destroy_functions;
} }
mutex_unlock(&data->irq_mutex);
return 0; return 0;
err_destroy_functions: err_destroy_functions:
rmi_free_function_list(rmi_dev); rmi_free_function_list(rmi_dev);
mutex_unlock(&data->irq_mutex);
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(rmi_init_functions);
static int rmi_driver_probe(struct device *dev) static int rmi_driver_probe(struct device *dev)
{ {
...@@ -1100,6 +1139,10 @@ static int rmi_driver_probe(struct device *dev) ...@@ -1100,6 +1139,10 @@ static int rmi_driver_probe(struct device *dev)
if (retval) if (retval)
goto err; goto err;
retval = rmi_f34_create_sysfs(rmi_dev);
if (retval)
goto err;
if (data->input) { if (data->input) {
rmi_driver_set_input_name(rmi_dev, data->input); rmi_driver_set_input_name(rmi_dev, data->input);
if (!rmi_dev->xport->input) { if (!rmi_dev->xport->input) {
...@@ -1117,7 +1160,7 @@ static int rmi_driver_probe(struct device *dev) ...@@ -1117,7 +1160,7 @@ static int rmi_driver_probe(struct device *dev)
if (data->f01_container->dev.driver) if (data->f01_container->dev.driver)
/* Driver already bound, so enable ATTN now. */ /* Driver already bound, so enable ATTN now. */
return enable_sensor(rmi_dev); return rmi_enable_sensor(rmi_dev);
return 0; return 0;
......
...@@ -95,12 +95,36 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, ...@@ -95,12 +95,36 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
bool rmi_is_physical_driver(struct device_driver *); bool rmi_is_physical_driver(struct device_driver *);
int rmi_register_physical_driver(void); int rmi_register_physical_driver(void);
void rmi_unregister_physical_driver(void); void rmi_unregister_physical_driver(void);
void rmi_free_function_list(struct rmi_device *rmi_dev);
int rmi_enable_sensor(struct rmi_device *rmi_dev);
int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
int (*callback)(struct rmi_device *rmi_dev, void *ctx,
const struct pdt_entry *entry));
int rmi_probe_interrupts(struct rmi_driver_data *data);
int rmi_init_functions(struct rmi_driver_data *data);
int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
const struct pdt_entry *pdt);
char *rmi_f01_get_product_ID(struct rmi_function *fn); char *rmi_f01_get_product_ID(struct rmi_function *fn);
#ifdef CONFIG_RMI4_F34
int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev);
#else
static inline int rmi_f34_create_sysfs(struct rmi_device *rmi_dev)
{
return 0;
}
static inline void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev)
{
}
#endif /* CONFIG_RMI_F34 */
extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f01_handler;
extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f11_handler;
extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f12_handler;
extern struct rmi_function_handler rmi_f30_handler; extern struct rmi_function_handler rmi_f30_handler;
extern struct rmi_function_handler rmi_f34_handler;
extern struct rmi_function_handler rmi_f54_handler; extern struct rmi_function_handler rmi_f54_handler;
#endif #endif
...@@ -63,6 +63,8 @@ struct f01_basic_properties { ...@@ -63,6 +63,8 @@ struct f01_basic_properties {
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) #define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
/* The device has lost its configuration for some reason. */ /* The device has lost its configuration for some reason. */
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) #define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
/* The device is in bootloader mode */
#define RMI_F01_STATUS_BOOTLOADER(status) ((status) & 0x40)
/* Control register bits */ /* Control register bits */
...@@ -594,6 +596,10 @@ static int rmi_f01_attention(struct rmi_function *fn, ...@@ -594,6 +596,10 @@ static int rmi_f01_attention(struct rmi_function *fn,
return error; return error;
} }
if (RMI_F01_STATUS_BOOTLOADER(device_status))
dev_warn(&fn->dev,
"Device in bootloader mode, please update firmware\n");
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
dev_warn(&fn->dev, "Device reset detected.\n"); dev_warn(&fn->dev, "Device reset detected.\n");
error = rmi_dev->driver->reset_handler(rmi_dev); error = rmi_dev->driver->reset_handler(rmi_dev);
......
This diff is collapsed.
/*
* Copyright (c) 2007-2016, Synaptics Incorporated
* Copyright (C) 2016 Zodiac Inflight Innovations
*
* 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.
*/
#ifndef _RMI_F34_H
#define _RMI_F34_H
/* F34 image file offsets. */
#define F34_FW_IMAGE_OFFSET 0x100
/* F34 register offsets. */
#define F34_BLOCK_DATA_OFFSET 2
/* F34 commands */
#define F34_WRITE_FW_BLOCK 0x2
#define F34_ERASE_ALL 0x3
#define F34_READ_CONFIG_BLOCK 0x5
#define F34_WRITE_CONFIG_BLOCK 0x6
#define F34_ERASE_CONFIG 0x7
#define F34_ENABLE_FLASH_PROG 0xf
#define F34_STATUS_IN_PROGRESS 0xff
#define F34_STATUS_IDLE 0x80
#define F34_IDLE_WAIT_MS 500
#define F34_ENABLE_WAIT_MS 300
#define F34_ERASE_WAIT_MS 5000
#define F34_BOOTLOADER_ID_LEN 2
struct rmi_f34_firmware {
__le32 checksum;
u8 pad1[3];
u8 bootloader_version;
__le32 image_size;
__le32 config_size;
u8 product_id[10];
u8 product_info[2];
u8 pad2[228];
u8 data[];
};
struct f34v5_data {
u16 block_size;
u16 fw_blocks;
u16 config_blocks;
u16 ctrl_address;
u8 status;
struct completion cmd_done;
struct mutex flash_mutex;
};
struct f34_data {
struct rmi_function *fn;
unsigned char bootloader_id[5];
unsigned char configuration_id[9];
struct f34v5_data v5;
};
#endif /* _RMI_F34_H */
...@@ -337,11 +337,13 @@ struct rmi_driver_data { ...@@ -337,11 +337,13 @@ struct rmi_driver_data {
struct rmi_device *rmi_dev; struct rmi_device *rmi_dev;
struct rmi_function *f01_container; struct rmi_function *f01_container;
struct rmi_function *f34_container;
bool f01_bootloader_mode; bool f01_bootloader_mode;
u32 attn_count; u32 attn_count;
int num_of_irq_regs; int num_of_irq_regs;
int irq_count; int irq_count;
void *irq_memory;
unsigned long *irq_status; unsigned long *irq_status;
unsigned long *fn_irq_bits; unsigned long *fn_irq_bits;
unsigned long *current_irq_mask; unsigned long *current_irq_mask;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment