Skip to content

Instantly share code, notes, and snippets.

@xqms
Created October 12, 2014 19:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xqms/086bec7269df64f46b14 to your computer and use it in GitHub Desktop.
Save xqms/086bec7269df64f46b14 to your computer and use it in GitHub Desktop.
i2c-rk3x clock rate change tester
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
// 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