Created
October 12, 2014 19:04
-
-
Save xqms/086bec7269df64f46b14 to your computer and use it in GitHub Desktop.
i2c-rk3x clock rate change tester
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ifneq ($(KERNELRELEASE),) | |
obj-m := i2c-clk-tester.o | |
i2c-clk-tester-y := tester.o | |
else | |
KDIR ?= /lib/modules/`uname -r`/build | |
default: | |
$(MAKE) -C $(KDIR) M=$$PWD | |
endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I2C test driver | |
#include <linux/kernel.h> | |
#include <linux/module.h> | |
#include <linux/i2c.h> | |
#include <linux/clk.h> | |
#include <linux/clk-provider.h> | |
#include <linux/gpio.h> | |
#define TRIGGER_GPIO 7 /* GPIO0_A7 */ | |
#define TRIGGER2_GPIO 6 /* GPIO0_A6 */ | |
static u8 test_buf[256]; | |
static struct i2c_msg test_messages[] = { | |
{ | |
.addr = 0x74, | |
.flags = I2C_M_IGNORE_NAK, | |
.len = 100, | |
.buf = test_buf | |
} | |
}; | |
static struct clk *i2c_clk; | |
static unsigned long base_rate; | |
static bool slow = false; | |
static struct i2c_adapter* adap; | |
static struct workqueue_struct *work_queue = 0; | |
static volatile bool shutting_down = false; | |
static void do_transfer(struct work_struct *work); | |
static void do_clk(struct work_struct *work); | |
DECLARE_WORK(transfer_work, do_transfer); | |
DECLARE_DELAYED_WORK(clk_work, do_clk); | |
static void do_transfer(struct work_struct *work) | |
{ | |
i2c_transfer(adap, test_messages, 1); | |
if (!shutting_down) | |
queue_work(work_queue, &transfer_work); | |
} | |
static void do_clk(struct work_struct *work) | |
{ | |
int ret; | |
if (slow) { | |
gpio_set_value(TRIGGER_GPIO, 1); | |
ret = clk_set_rate(i2c_clk, base_rate); | |
gpio_set_value(TRIGGER2_GPIO, 1); | |
} else { | |
gpio_set_value(TRIGGER_GPIO, 0); | |
ret = clk_set_rate(i2c_clk, base_rate/2); | |
gpio_set_value(TRIGGER2_GPIO, 0); | |
} | |
if (ret) { | |
pr_err("Could not change i2c clk rate: %d", ret); | |
return; | |
} | |
slow = !slow; | |
if (!shutting_down) { | |
queue_delayed_work(work_queue, &clk_work, 11 * HZ / 1000); | |
} | |
} | |
static int __init tester_init(void) | |
{ | |
int ret = 0; | |
adap = i2c_get_adapter(0); | |
if (!adap) { | |
pr_err("Could not find I2C-0\n"); | |
return -EIO; | |
} | |
i2c_clk = __clk_lookup("pclk_i2c0"); | |
if (IS_ERR(i2c_clk)) { | |
pr_err("Could not find pclk_i2c0 clock: %ld\n", PTR_ERR(i2c_clk)); | |
return -EIO; | |
} | |
gpio_request(TRIGGER_GPIO, "i2c-test-trigger"); | |
gpio_direction_output(TRIGGER_GPIO, 0); | |
gpio_request(TRIGGER2_GPIO, "i2c-test-trigger2"); | |
gpio_direction_output(TRIGGER2_GPIO, 0); | |
base_rate = clk_get_rate(i2c_clk); | |
work_queue = alloc_workqueue("i2c-test", WQ_UNBOUND, 0); | |
if (IS_ERR(work_queue)) { | |
pr_err("Could not allocate work queue: %d\n", ret); | |
goto err_gpio; | |
} | |
queue_work(work_queue, &transfer_work); | |
queue_delayed_work(work_queue, &clk_work, 0); | |
return ret; | |
err_gpio: | |
gpio_free(TRIGGER_GPIO); | |
gpio_free(TRIGGER2_GPIO); | |
clk_put(i2c_clk); | |
return -EIO; | |
} | |
module_init(tester_init); | |
static void tester_exit(void) | |
{ | |
shutting_down = true; | |
cancel_work_sync(&transfer_work); | |
cancel_delayed_work_sync(&clk_work); | |
destroy_workqueue(work_queue); | |
gpio_free(TRIGGER_GPIO); | |
gpio_free(TRIGGER2_GPIO); | |
clk_set_rate(i2c_clk, base_rate); | |
i2c_put_adapter(adap); | |
} | |
module_exit(tester_exit); | |
MODULE_AUTHOR("Max Schwarz <max.schwarz@online.de>"); | |
MODULE_DESCRIPTION("I2C tester module"); | |
MODULE_LICENSE("GPL"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment