drv8424_mmi: support variable speed sequences

Support for accelerated motion. Only distinct status reported.
Added position interface to shutdown motor based on signal from
sensors

Change-Id: I44f7eeb4b86f84c2542c0c29feec42bb7b346e16
Signed-off-by: Konstantin Makariev <hcv867@motorola.com>
Reviewed-on: https://gerrit.mot.com/2231441
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Qing Chang <qing@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Konstantin Makariev 2022-03-31 17:34:28 -05:00 • committed by Konstantin Makariev
commit 9352766163
2 changed files with 215 additions and 328 deletions

View file

@ -1,215 +0,0 @@
&tlmm {
drv8424_gpios {
drv8424_mode0_low: drv8424_mode0_low {
mux {
pins = "gpio70";
function = "gpio";
};
config {
pins = "gpio70";
drive-strength = <2>; /* 2 MA */
output-low;
};
};
drv8424_mode0_high: drv8424_mode0_high {
mux {
pins = "gpio70";
function = "gpio";
};
config {
pins = "gpio70";
drive-strength = <2>; /* 2 MA */
output-high;
};
};
drv8424_mode0_disable: drv8424_mode0_disable {
mux {
pins = "gpio70";
function = "gpio";
};
config {
pins = "gpio70";
input-enable;
bias-disable;
};
};
drv8424_mode1_low: drv8424_mode1_low {
mux {
pins = "gpio71";
function = "gpio";
};
config {
pins = "gpio71";
drive-strength = <2>; /* 2 MA */
output-low;
};
};
drv8424_mode1_high: drv8424_mode1_high {
mux {
pins = "gpio71";
function = "gpio";
};
config {
pins = "gpio71";
drive-strength = <2>; /* 2 MA */
output-high;
};
};
drv8424_mode1_disable: drv8424_mode1_disable {
mux {
pins = "gpio71";
function = "gpio";
};
config {
pins = "gpio71";
input-enable;
bias-disable;
};
};
drv8424_t0_low: drv8424_t0_low {
mux {
pins = "gpio49";
function = "gpio";
};
config {
pins = "gpio49";
drive-strength = <2>; /* 2 MA */
output-low;
};
};
drv8424_t0_high: drv8424_t0_high {
mux {
pins = "gpio49";
function = "gpio";
};
config {
pins = "gpio49";
drive-strength = <2>; /* 2 MA */
output-high;
};
};
drv8424_t0_disable: drv8424_t0_disable {
mux {
pins = "gpio49";
function = "gpio";
};
config {
pins = "gpio49";
input-enable;
bias-disable;
};
};
drv8424_t1_low: drv8424_t1_low {
mux {
pins = "gpio33";
function = "gpio";
};
config {
pins = "gpio33";
drive-strength = <2>; /* 2 MA */
output-low;
};
};
drv8424_t1_high: drv8424_t1_high {
mux {
pins = "gpio33";
function = "gpio";
};
config {
pins = "gpio33";
drive-strength = <2>; /* 2 MA */
output-high;
};
};
drv8424_t1_disable: drv8424_t1_disable {
mux {
pins = "gpio33";
function = "gpio";
};
config {
pins = "gpio33";
input-enable;
bias-disable;
};
};
drv8424_int_default: drv8424_int_default {
mux {
pins = "gpio57";
function = "gpio";
};
config {
pins = "gpio57";
drive-strength = <6>;
input-enable;
bias-disable;
};
};
drv8424_clk_active: drv8424_clk_active {
mux {
pins = "gpio21";
function = "gcc_gp2";
};
config {
pins = "gpio21";
drive-strength = <2>; /* 2 MA */
bias-pull-none;
};
};
drv8424_clk_sleep: drv8424_clk_sleep {
mux {
pins = "gpio21";
function = "gpio";
};
config {
pins = "gpio21";
/* drive-strength = <2>; */ /* 2 MA */
/* bias-pull-down; */
input-enable;
bias-disable;
};
};
};
};
&soc {
moto_drv8424_support {
compatible = "moto,drv8424";
status = "ok"
pinctrl-names = "m0_low", "m0_high", "m0_disable",
"m1_low", "m1_high", "m1_disable",
"t0_low", "t0_high", "t0_disable",
"t1_low", "t1_high", "t1_disable",
"drv8424_int_default",
"drv8424_clk_active", "drv8424_clk_sleep";
pinctrl-0 = <&drv8424_mode0_low>;
pinctrl-1 = <&drv8424_mode0_high>;
pinctrl-2 = <&drv8424_mode0_disable>;
pinctrl-3 = <&drv8424_mode1_low>;
pinctrl-4 = <&drv8424_mode1_high>;
pinctrl-5 = <&drv8424_mode1_disable>;
pinctrl-6 = <&drv8424_t0_low>;
pinctrl-7 = <&drv8424_t0_high>;
pinctrl-8 = <&drv8424_t0_disable>;
pinctrl-9 = <&drv8424_t1_low>;
pinctrl-10 = <&drv8424_t1_high>;
pinctrl-11 = <&drv8424_t1_disable>;
pinctrl-12 = <&drv8424_int_default>;
pinctrl-13 = <&drv8424_clk_active>;
pinctrl-14 = <&drv8424_clk_sleep>;
interrupt-parent = <&tlmm>;
interrupts = <57 0x2002>;
drv8424-gpios = <52 57 21 32 30 51 29 50 49 33>;
drv8424-gpios-cells = <10>;
drv8424-max-freq = <250000>;
clocks = <&clock_gcc 36>;
clock-names = "gcc_gp2";
vdd-supply = <&pm6150_l13>;
enable-hw-clock;
power-default-off;
};
};

View file

@ -58,9 +58,7 @@ static const char *position_labels[] = {
enum status_id {
STATUS_UNKNOWN,
STATUS_STOPPED_COMPACT,
STATUS_STOPPED_EXPANDED,
STATUS_STOPPED_PEEK,
STATUS_STOPPED,
STATUS_MOVING_IN,
STATUS_MOVING_OUT,
};
@ -176,6 +174,47 @@ static const char* const pins_state[] = {
NULL
};
enum regime_idx {
SQ_FULL,
SQ_SHORTENED,
SQ_SHORT,
SQ_TINY,
SQ_MAX
};
static const char *regime_names[SQ_MAX] = {
"FULL",
"SHORTENED",
"SHORT",
"TINY"
};
/* PPS Pulses
stage 1: 400 24
stage 2: 1400 56
stage 3: 3000 6252
stage 4: 2000 12
*/
#define MAX_STAGE_LEGS 10
typedef struct {
unsigned freq, ceiling;
} motor_stage;
static motor_stage initial_data[SQ_MAX][MAX_STAGE_LEGS] = {
{ /* FULL 44mm */
{400,24}, {1400,56}, {3000,6252}, {2000,12}
},
{ /* SHORTENED 39mm */
{400,24}, {1400,56}, {3000,5530}, {2000,12}
},
{ /* SHORT 5mm */
{400,24}, {1400,56}, {3000,630}, {2000,12}
},
{ /* TINY 1mm */
{400,24}, {1400,56}, {2000,65}
}
};
#define MAX_GPIOS MOTOR_UNKNOWN
typedef struct motor_control {
struct regulator *vdd;
@ -184,7 +223,7 @@ typedef struct motor_control {
struct pinctrl* pins;
struct pinctrl_state *pin_state[PINS_END];
const char* const * plabels;
}motor_control;
} motor_control;
#define CLOCK_NAME_LEN 16
typedef struct motor_device {
@ -217,6 +256,8 @@ typedef struct motor_device {
unsigned time_out;
unsigned half;
unsigned stage, max_stages;
unsigned regime;
motor_stage sequencer[SQ_MAX][MAX_STAGE_LEGS];
bool double_edge;
bool hw_clock;
bool power_default_off;
@ -237,7 +278,6 @@ typedef struct motor_device {
static int set_pinctrl_state(motor_device* md, unsigned state_index);
static void moto_drv8424_set_step_freq(motor_device* md, unsigned freq);
static bool motor_drive_stage_init(motor_device* md, int step_count);
static ktime_t adapt_time_helper(ktime_t usec)
{
@ -547,7 +587,7 @@ static void moto_drv8424_set_power_en(motor_device* md)
usleep_range(500, 1000);
/* signal sensor hub to start/stop sampling hall effect */
GPIO_OUTPUT_DIR(mc->ptable[MOTOR_ACTIVE], md->power_en);
LOGD("gpios BOOST & ACTIVE updated\n");
LOGD("gpios BOOST & ACTIVE set: %u\n", md->power_en);
}
//Power sequence: PowerEn On->nSleep On->nSleep Off->PowerEn Off
@ -580,6 +620,28 @@ static int moto_drv8424_set_power(motor_device* md, unsigned power)
exit:
return ret;
}
static void moto_drv8424_set_regime(motor_device *md, unsigned regime)
{
unsigned msn;
unsigned long flags;
for (msn = 0; msn < MAX_STAGE_LEGS; msn++) {
if (md->sequencer[regime][msn].freq && md->sequencer[regime][msn].ceiling)
continue;
break;
}
spin_lock_irqsave(&md->mlock, flags);
md->regime = regime;
md->stage = 0;
md->max_stages = msn;
spin_unlock_irqrestore(&md->mlock, flags);
dev_info(md->dev, "Set active regime: %s, stages # %u\n",
regime_names[md->regime], msn);
}
#if 0
static int moto_drv8424_enable_clk(motor_device* md, bool en)
{
@ -588,31 +650,45 @@ static int moto_drv8424_enable_clk(motor_device* md, bool en)
#endif
#ifdef MOTOR_SLOT_DBG
static ktime_t timesA[20];
static unsigned slot;
static void inline logtime_store(motor_device* md)
static void inline logtime_store(void)
{
if ((md->slot + 1) < sizeof(timesA)/sizeof(timesA[0])) {
timesA[md->slot++] = ktime_get();
if ((slot + 1) < sizeof(timesA)/sizeof(timesA[0])) {
timesA[slot++] = ktime_get();
}
}
static void logtime_show(motor_device* md)
static void logtime_show(void)
{
int i;
ktime_t diff;
for (i = 1; i < md->slot; i++) {
if (!slot)
return;
for (i = 1; i < slot; i++) {
diff = ktime_sub(timesA[i], timesA[i-1]);
pr_err("step[%d]=%lldus\n", i, ktime_to_us(diff));
}
}
#endif
static void moto_drv8424_drive_init(motor_device* md, int step_count)
{
md->slot = 0U;
atomic_set(&md->stepping, 1);
atomic_set(&md->step_count, step_count);
static void inline logtime_reset(void)
{
slot = 0U;
}
#else
#define logtime_store()
#define logtime_show()
#define logtime_reset()
#endif
static void moto_drv8424_drive_stage_init(motor_device* md)
{
logtime_reset();
atomic_set(&md->stepping, 1);
atomic_set(&md->step_count, 1);
md->double_edge = false;
md->half = md->step_period >> 1;
md->level = 1;
@ -621,22 +697,41 @@ static void moto_drv8424_drive_init(motor_device* md, int step_count)
|| md->mode == STEP_32_BOTH_EDEG) {
md->double_edge = true;
}
atomic_set(&md->status, md->dir ? STATUS_MOVING_IN : STATUS_MOVING_OUT);
}
static bool moto_drv8424_next_stage(motor_device* md)
{
unsigned freq;
motor_stage *ms;
if (++md->stage > md->max_stages) {
md->max_stages = 0;
md->stage = 0;
return false;
}
logtime_show();
ms = &md->sequencer[md->regime][md->stage - 1];
freq = ms->freq;
md->step_ceiling = ms->ceiling;
moto_drv8424_set_step_freq(md, freq);
moto_drv8424_drive_stage_init(md);
return true;
}
static int moto_drv8424_drive_sequencer(motor_device* md)
{
unsigned long flags;
spin_lock_irqsave(&md->mlock, flags);
moto_drv8424_drive_init(md, 0);
spin_unlock_irqrestore(&md->mlock, flags);
LOGD("ceiling %lu, freq %uHz period %luns, half %uns\n", md->step_ceiling, md->step_freq, md->step_period, md->half);
moto_drv8424_next_stage(md);
moto_drv8424_set_motor_torque(md);
moto_drv8424_set_motor_dir(md);
moto_drv8424_set_motor_mode(md);
moto_drv8424_set_motor_opmode(md);
LOGD("sequencer init done\n");
atomic_set(&md->status, md->dir ? STATUS_MOVING_IN : STATUS_MOVING_OUT);
LOGD("sequencer init: stages %u ceiling %lu, freq %uHz period %luns, half %uns\n",
md->max_stages, md->step_ceiling, md->step_freq, md->step_period, md->half);
if(atomic_read(&md->stepping)) {
motor_control* mc = &md->mc;
@ -644,9 +739,7 @@ static int moto_drv8424_drive_sequencer(motor_device* md)
GPIO_OUTPUT_DIR(mc->ptable[MOTOR_STEP], md->level);
atomic_inc(&md->step_count);
hrtimer_start(&md->stepping_timer, adapt_time_helper(md->half), HRTIMER_MODE_REL);
#ifdef MOTOR_SLOT_DBG
logtime_store(md);
#endif
logtime_store();
}
return 0;
@ -674,7 +767,7 @@ static __ref int motor_kthread(void *arg)
sched_setscheduler(current, SCHED_FIFO, &param);
while (!kthread_should_stop()) {
dev_info(md->dev, "wait for event\n");
LOGD("wait for event\n");
do {
ret = wait_event_interruptible(md->sync_complete,
md->user_sync_complete || kthread_should_stop());
@ -689,7 +782,7 @@ static __ref int motor_kthread(void *arg)
dev_info(md->dev, "vdd power off\n");
moto_drv8424_set_regulator_power(md, false);
}
#if 0
switch (atomic_read(&md->position)) {
case POS_COMPACT:
value = STATUS_STOPPED_COMPACT;
@ -702,13 +795,13 @@ static __ref int motor_kthread(void *arg)
break;
default: value = STATUS_UNKNOWN;
}
#endif
value = STATUS_STOPPED;
atomic_set(&md->status, value);
dev_info(md->dev, "status %d, position %d\n",
LOGD("status %d, position %d\n",
atomic_read(&md->status), atomic_read(&md->position));
sysfs_notify(&md->dev->kobj, NULL, "status");
#ifdef MOTOR_SLOT_DBG
logtime_show(md);
#endif
logtime_show();
}
return 0;
@ -756,12 +849,8 @@ static enum hrtimer_restart motor_stepping_timer_action(struct hrtimer *h)
spin_lock_irqsave(&md->mlock, flags);
if(!atomic_read(&md->stepping)) {
motor_stop(md, true);
GPIO_OUTPUT_DIR(md->mc.ptable[MOTOR_STEP], 0);
spin_unlock_irqrestore(&md->mlock, flags);
return HRTIMER_NORESTART;
}
if(!atomic_read(&md->stepping))
goto next_stage;
md->level = !md->level;
GPIO_OUTPUT_DIR(md->mc.ptable[MOTOR_STEP], md->level);
@ -771,12 +860,13 @@ static enum hrtimer_restart motor_stepping_timer_action(struct hrtimer *h)
} else if(md->level) {
atomic_inc(&md->step_count);
}
#ifdef MOTOR_SLOT_DBG
logtime_store(md); /* log timestamp */
#endif
logtime_store();
if(md->step_ceiling && atomic_read(&md->step_count) > md->step_ceiling) {
next_stage:
/* multi stage sequence */
if (!(md->max_stages && motor_drive_stage_init(md, 1))) {
if (!moto_drv8424_next_stage(md)) {
motor_stop(md, true);
GPIO_OUTPUT_DIR(md->mc.ptable[MOTOR_STEP], 0);
spin_unlock_irqrestore(&md->mlock, flags);
@ -1176,112 +1266,117 @@ exit:
return len;
}
/* PPS Pulses
stage 1: 400 24
stage 2: 1400 56
stage 3: 3000 6252
stage 4: 2000 12
*/
#define MAX_STAGE_LEGS 10
struct stage_data {
unsigned freq, ceiling;
} multi_stage_sequencer[MAX_STAGE_LEGS];
static int motor_stage_sequence_init(char *buffer, size_t len, motor_device* md)
{
char *next, *pair = buffer;
int msn;
unsigned freq, ceiling;
for (msn = 0; pair && msn < MAX_STAGE_LEGS; msn++, pair = next ) {
next = strnchr(pair, len, ' ');
if (next) {
*next++ = '\0';
len -= strlen(pair);
}
if (sscanf(pair, "%u:%u", &freq, &ceiling) == 2) {
multi_stage_sequencer[msn].freq = freq;
multi_stage_sequencer[msn].ceiling = ceiling;
dev_info(md->dev, "stage[%d]: freq=%u, ceiling=%u\n", msn, freq, ceiling);
}
}
return msn;
}
static bool motor_drive_stage_init(motor_device* md, int step_count)
{
unsigned freq;
if (++md->stage > md->max_stages) {
md->max_stages = 0;
md->stage = 0;
return false;
}
#ifdef MOTOR_SLOT_DBG
if (md->slot)
logtime_show(md);
#endif
freq = multi_stage_sequencer[md->stage - 1].freq;
md->step_ceiling = multi_stage_sequencer[md->stage - 1].ceiling;
//LOGD("stage %u, freq %u, ceiling %lu\n", md->stage, freq, md->step_ceiling);
//FIXME the next function calls spinlock!!!
moto_drv8424_set_step_freq(md, freq);
moto_drv8424_drive_init(md, step_count);
return true;
}
/* main interface to control motor via sysfs */
/* valid parameter values: 0 - roll in, 1 - roll out */
static ssize_t motor_command_store(struct device *dev, struct device_attribute *attr,
/* store new motor sequencer */
/* Format: index freq0:ceiling0 [... freqN:ceilingN] */
/* Example: 0 400:24 1400:56 3000:6252 2000:12 */
static ssize_t motor_sequencer_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
motor_device* md = (motor_device*)dev_get_drvdata(dev);
//unsigned long flags;
char *buffer;
if(md->faulting) {
dev_err(dev, "Device faulting\n");
return -EBUSY;
}
char *buffer, *next, *pair;
unsigned msn = 0;
unsigned value, idx, freq, ceiling;
buffer = kmalloc(len + 1, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
strlcpy(buffer, buf, len);
buffer[len + 1] = '\0';
md->max_stages = motor_stage_sequence_init(buffer, len, md);
next = strnchr(buffer, len, ' ');
if(kstrtouint(buffer, 10, &value) ||
(value < 0 || value >= SQ_MAX)) {
dev_err(dev, "Invalid index: %s\n", buffer);
goto exit;
}
idx = value;
for (msn = 0, pair = next; pair && msn < MAX_STAGE_LEGS; msn++, pair = next ) {
next = strnchr(pair, len, ' ');
if (next) {
*next++ = '\0';
len -= strlen(pair);
}
if (sscanf(pair, "%u:%u", &freq, &ceiling) == 2) {
md->sequencer[idx][msn].freq = freq;
md->sequencer[idx][msn].ceiling = ceiling;
dev_info(md->dev, "[%u][%u]: freq=%u, ceiling=%u\n", idx, msn, freq, ceiling);
}
}
exit:
md->max_stages = msn;
kfree(buffer);
if (md->max_stages == 0) {
dev_err(dev, "Error value: %s\n", buf);
return -EINVAL;
}
LOGD("max_stages %u\n", md->max_stages);
//spin_lock_irqsave(&md->mlock, flags);
/* hrtimer controlled stepper mechanism */
md->stage = 0;
motor_drive_stage_init(md, 0);
//spin_unlock_irqrestore(&md->mlock, flags);
moto_drv8424_drive_sequencer(md);
return len;
}
static ssize_t motor_sequencer_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
motor_device* md = (motor_device*)dev_get_drvdata(dev);
ssize_t blen = 0;
unsigned idx, msn;
for (idx = 0; idx < SQ_MAX; idx++) {
blen += snprintf(buf + blen, PAGE_SIZE - blen, "regime %s: ",
regime_names[idx]);
for (msn = 0; msn < MAX_STAGE_LEGS; msn++) {
if (md->sequencer[idx][msn].freq == 0 &&
md->sequencer[idx][msn].ceiling == 0)
continue;
blen += snprintf(buf + blen, PAGE_SIZE - blen, "%u:%u ",
md->sequencer[idx][msn].freq,
md->sequencer[idx][msn].ceiling);
}
blen += snprintf(buf + blen, PAGE_SIZE - blen, "\n");
}
return blen;
}
static ssize_t motor_regime_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
motor_device* md = (motor_device*)dev_get_drvdata(dev);
unsigned value;
if(kstrtouint(buf, 10, &value) ||
(value < SQ_FULL || value >= SQ_MAX)) {
dev_err(dev, "Error value: %s\n", buf);
return -EINVAL;
}
moto_drv8424_set_regime(md, value);
return len;
}
static ssize_t motor_regime_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
motor_device* md = (motor_device*)dev_get_drvdata(dev);
return snprintf(buf, 20, "%s\n", regime_names[md->regime]);
}
static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, motor_enable_show, motor_enable_store);
static DEVICE_ATTR(dir, S_IRUGO|S_IWUSR|S_IWGRP, motor_dir_show, motor_dir_store);
static DEVICE_ATTR(step, S_IRUGO|S_IWUSR|S_IWGRP, motor_step_show, motor_step_store);
static DEVICE_ATTR(ceiling, S_IRUGO|S_IWUSR|S_IWGRP, motor_ceiling_show, motor_ceiling_store);
static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR|S_IWGRP, motor_mode_show, motor_mode_store);
static DEVICE_ATTR(sequencer, S_IRUGO|S_IWUSR|S_IWGRP, motor_sequencer_show, motor_sequencer_store);
static DEVICE_ATTR(torque, S_IRUGO|S_IWUSR|S_IWGRP, motor_torque_show, motor_torque_store);
static DEVICE_ATTR(time_out, S_IRUGO|S_IWUSR|S_IWGRP, motor_time_out_show, motor_time_out_store);
static DEVICE_ATTR(reset, S_IRUGO|S_IWUSR|S_IWGRP, NULL, motor_reset_store);
static DEVICE_ATTR(command, S_IRUGO|S_IWUSR|S_IWGRP, NULL, motor_command_store);
static DEVICE_ATTR(regime, S_IRUGO|S_IWUSR|S_IWGRP, motor_regime_show, motor_regime_store);
#define ATTRS_STATIC 7
#define ATTRS_MAX 10
#define ATTRS_STATIC 8
#define ATTRS_MAX 11
static int last_idx = ATTRS_STATIC;
@ -1292,7 +1387,8 @@ static struct attribute *motor_attributes[ATTRS_MAX + 1] = {
&dev_attr_ceiling.attr,
&dev_attr_reset.attr,
&dev_attr_time_out.attr,
&dev_attr_command.attr,
&dev_attr_regime.attr,
&dev_attr_sequencer.attr,
NULL
};
@ -1427,7 +1523,7 @@ static int moto_drv8424_probe(struct platform_device *pdev)
{
struct device* dev = &pdev->dev;
motor_device* md;
int i = MOTOR_POWER_EN;
int i;
int ret = 0;
md = kzalloc(sizeof(motor_device), GFP_KERNEL);
@ -1444,6 +1540,10 @@ static int moto_drv8424_probe(struct platform_device *pdev)
moto_drv8424_set_step_freq(md, DEFAULT_STEP_FREQ);
md->time_out = MOTOR_DEFAULT_EXPIRE;
/* populate sequencer with initial values */
for (i = SQ_FULL; i < SQ_MAX; i++)
memcpy(md->sequencer[i], initial_data[i], sizeof(initial_data[i]));
/* assign default values for optional parameters */
md->mode = FULL_STEP;
md->torque = TORQUE_FULL;
@ -1477,8 +1577,9 @@ static int moto_drv8424_probe(struct platform_device *pdev)
md->user_sync_complete = false;
init_waitqueue_head(&md->sync_complete);
atomic_set(&md->position, POS_COMPACT);
atomic_set(&md->status, STATUS_STOPPED_COMPACT);
atomic_set(&md->position, POS_UNKNOWN);
//atomic_set(&md->status, STATUS_STOPPED_COMPACT);
atomic_set(&md->status, STATUS_STOPPED);
md->motor_task = kthread_create(motor_kthread, md, "motor_task");
if (IS_ERR(md->motor_task)) {
ret = PTR_ERR(md->motor_task);
@ -1545,6 +1646,7 @@ static int moto_drv8424_probe(struct platform_device *pdev)
/*Here motor can work, but have not irq*/
dev_info(dev, "Failed to set device irq\n");
}
dev_info(dev, "Success init device\n");
return 0;