sdk-hwV1.3/lichee/linux-4.9/drivers/pwm/pwm-xy-motor.c

889 lines
22 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* drivers/pwm/pwm-sunxi-dev.c
*
* Allwinnertech pulse-width-modulation controller driver
*
* Copyright (C) 2019 AllWinner
*
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define PWM_ERR(fmt, arg...) pr_err("%s()%d - " fmt, __func__, __LINE__, ##arg)
#define DEBUG
#ifdef DEBUG
#define MOTOR_DEBUG(fmt, arg...) \
printk("%s()%d - " fmt, __func__, __LINE__, ##arg)
#else
#define MOTOR_DEBUG(fmt, arg...)
#endif
/* commands' magic number */
#define SSP_DRV_MAGICNUM 'm'
/* commands' number */
#define NR_SSP_MOTOR_POSITION_GET 1
#define NR_SSP_DIR_SPEED_SET 2
#define NR_SSP_MOTOR_STOP 3
#define NR_SSP_MOTOR_RUNNED_TIME_GET 4
#define NR_SSP_MOTOR_STEP_SPEED_SET 5
/* cmd maker */
#define SSP_IOCTL_CMD_MAKE(cmd) _IO(SSP_DRV_MAGICNUM, cmd)
/* export */
/* cmds */
#define SSP_MOTOR_POSITION_GET SSP_IOCTL_CMD_MAKE(NR_SSP_MOTOR_POSITION_GET)
#define SSP_DIR_SPEED_SET SSP_IOCTL_CMD_MAKE(NR_SSP_DIR_SPEED_SET)
#define SSP_MOTOR_STOP SSP_IOCTL_CMD_MAKE(NR_SSP_MOTOR_STOP)
#define SSP_MOTOR_RUNNED_TIME_GET \
SSP_IOCTL_CMD_MAKE(NR_SSP_MOTOR_RUNNED_TIME_GET)
#define SSP_MOTOR_STEP_SPEED_SET SSP_IOCTL_CMD_MAKE(NR_SSP_MOTOR_STEP_SPEED_SET)
/*--------------logic contril parameters defined--------------*/
/* motor_ab -->x ; motor_cd-->y*/
#define MOTOR_AB 1
#define MOTOR_CD 2
/* step range set, 24BYJ48-550 motor: 4096 setp/r */
//need to 8 step align
#define AB_STEPS 8192
#define CD_STEPS 1088
/*define direction*/
#define X_MOTOR_FORWARD 0
#define X_MOTOR_BACKWARD 1
#define Y_MOTOR_FORWARD 1
#define Y_MOTOR_BACKWARD 0
#define DIR_NONE 0
#define DIR_UP 1
#define DIR_DOWN 2
#define DIR_LEFT 4
#define DIR_RIGHT 3
#define SPEED_NUM_MAX 11
/*This array can get motor speed (ns/step) */
int motor_speed_table[SPEED_NUM_MAX] = {1465, 1413, 1361,
1309, 1257, 1205,
1153, 1101, 1049,
997, 997}; /* ns/step */
/*--------------struct defined-------------------*/
struct pwm_config_group {
int group_channel;
int group_run_count;
int pwm_polarity;
int pwm_period;
};
struct motor_pwm_dev {
struct device *dev;
int x_limit_gpio;
int x_init;
int y_init;
int init_flag;
struct motor_status *ab;
struct motor_status *cd;
};
/* this struct is motor runing status*/
struct motor_status {
int index;
int direction;
int speed;
int is_running;
int max_step;
int irq;
struct timeval start_time;
struct timeval end_time;
struct hrtimer timer;
struct work_struct init_work;
struct work_struct motor_work;
};
struct pzt_ctrl {
/* x,y is position,
* fat left meant x =0 , to right will x++
* fat up meant y =0 , to down will y++ */
int x;
int y;
/* dir is direction:
* 0:none
* 1:up
* 2:down
* 3:right
* 4:left*/
unsigned int dir;
/*set motor speed 0~10*/
unsigned int speed;
/*motor running time ,it can conversion step*/
unsigned int run_time;
/*motor run status
* 0: stop
* 1: running*/
unsigned int is_running;
/* this will report motor running time of sum
* motor_runned_time[0] : ab motor
* motor_runned_time[0] : cd motor*/
unsigned int motor_runned_time[2];
};
/*--------------global variable-------------------*/
struct motor_pwm_dev *motor_pwm_dev;
struct motor_status motor_status_ab = {
.index = MOTOR_AB,
.max_step = AB_STEPS,
};
struct motor_status motor_status_cd = {
.index = MOTOR_CD,
.max_step = CD_STEPS
};
struct pzt_ctrl ptz_ctrl_k2u;
struct pzt_ctrl ptz_ctrl_u2k;
struct pwm_device *pwm[8] = {NULL};
/*--------------motor pwm interface-------------------*/
static int motor_start(int index, int step, int polarity, int step_ns)
{
unsigned int ret, i, group;
unsigned char name[30];
struct pwm_config_group *code_group;
struct pwm_config_group config = {1, 1, 0, 1000};
config.group_channel = index;
/* because motor will run 8 step earch period */
config.group_run_count = step / 8;
config.pwm_polarity = polarity;
config.pwm_period = step_ns * 8 / 10;
code_group = &config;
group = code_group->group_channel;
if (group < 1) {
return -EINVAL;
}
for (i = 4 * (group - 1); i < 4 * group; i++) {
sprintf(name, "sunxi_pwm%d", i);
if (pwm[i] == NULL) {
pwm[i] = pwm_request(i, name);
if (IS_ERR_OR_NULL(pwm[i])) {
PWM_ERR("pwm err\n");
return -ENODEV;
}
}
pwm[i]->chip_data = code_group;
/* the argument cant be same as the first */
ret = pwm_config(pwm[i], 0, 1);
if (ret < 0) {
PWM_ERR("pwm ioctl err0\n");
return -EINVAL;
}
ret = pwm_config(pwm[i], 0x2ee, 0x7cf);
if (ret < 0) {
PWM_ERR("pwm ioctl err\n");
return -EINVAL;
}
pwm_enable(pwm[i]);
}
return 0;
}
static int motor_stop(int index)
{
int i;
unsigned int group;
struct pwm_config_group *code_group;
struct pwm_config_group config = {1, 1, 0, 1000};
config.group_channel = index;
code_group = &config;
group = code_group->group_channel;
if (group < 1) {
PWM_ERR("group para err\n");
return -EINVAL;
}
for (i = 4 * (group - 1); i < 4 * group; i++) {
if (pwm[i] == NULL) {
PWM_ERR("PWM[%d] is NULL\n", i);
return -1;
}
pwm[i]->chip_data = code_group;
if (pwm[i]) {
pwm_disable(pwm[i]);
}
}
return 0;
}
/*-------------motor logic control interface------------------*/
static int xy_motor_position_init(void)
{
#if 0
/* let x/y to find 0 postition*/
schedule_work(&motor_pwm_dev->ab->init_work);
schedule_work(&motor_pwm_dev->cd->init_work);
#endif
return 0;
}
static int xy_motor_stop_and_get_position(int index)
{
struct motor_status *status;
unsigned int time;
unsigned int max_time;
int p_offset;
#if 0
if (!motorPwmPev->init_flag) {
pr_err("motor position was not init \n");
return -1;
}
#endif
if (index == MOTOR_AB)
status = &motor_status_ab;
else if (index == MOTOR_CD)
status = &motor_status_cd;
else
return -1;
/*checking the status*/
if (!status->is_running || !status->start_time.tv_usec)
return -1;
/*stop timer*/
hrtimer_cancel(&status->timer);
/*stop motor*/
motor_stop(status->index);
/* get end time */
do_gettimeofday(&status->end_time);
/* get motor running time ns*/
time = (status->end_time.tv_sec - status->start_time.tv_sec) * 1000000 +
(status->end_time.tv_usec - status->start_time.tv_usec);
/*get position_offset = time / speed*/
p_offset = time / motor_speed_table[status->speed];
/* update data */
if (status->index == MOTOR_AB) {
if (status->direction == X_MOTOR_FORWARD)
ptz_ctrl_k2u.x += p_offset;
else
ptz_ctrl_k2u.x -= p_offset;
#ifdef GPIO_LIMIT_DET
/* x limit checking */
if (!gpio_get_value_cansleep(motor_pwm_dev->x_limit_gpio)) {
/* x already in limit positions*/
if (status->direction == Y_MOTOR_FORWARD)
ptz_ctrl_k2u.x = status->max_step;
else
ptz_ctrl_k2u.x = 0;
}
#else
/* x limit checking */
/* x already in limit positions*/
max_time = status->max_step * motor_speed_table[motor_status_ab.speed];
if (max_time == time || max_time > time)
if (status->direction == X_MOTOR_FORWARD)
ptz_ctrl_k2u.x = status->max_step;
else
ptz_ctrl_k2u.x = 0;
#endif
ptz_ctrl_k2u.motor_runned_time[0] = time / 1000;
} else if (status->index == MOTOR_CD) {
if (status->direction == Y_MOTOR_FORWARD)
ptz_ctrl_k2u.y += p_offset;
else
ptz_ctrl_k2u.y -= p_offset;
/* y limit checking */
/* y already in limit positions*/
max_time = status->max_step * motor_speed_table[motor_status_cd.speed];
if (max_time == time || max_time > time)
if (status->direction == Y_MOTOR_FORWARD)
ptz_ctrl_k2u.y = status->max_step;
else
ptz_ctrl_k2u.y = 0;
ptz_ctrl_k2u.motor_runned_time[1] = time / 1000;
}
/*clean status*/
status->start_time.tv_usec = 0;
status->end_time.tv_usec = 0;
status->is_running = 0;
MOTOR_DEBUG("stop motor :%d, cost time:%d ms \n", status->index, (time/1000));
MOTOR_DEBUG("Now x:%d, y:%d\n", ptz_ctrl_k2u.x, ptz_ctrl_k2u.y);
return 0;
}
#if 0
static int xy_motor_position_set(int index, int step_pos)
{
int ret;
int move_step;
if (!motor_pwm_dev->init_flag) {
return -1;
pr_err("motor position was not init \n");
}
/*first stop motor*/
motor_stop(index);
if (index == MOTOR_AB) {
move_step = step_pos - ptz_ctrl_k2u.x;
/*save the x final position*/
ptz_ctrl_k2u.x = step_pos;
} else if (index == MOTOR_CD) {
move_step = step_pos - ptz_ctrl_k2u.y;
/*save the y final position*/
ptz_ctrl_k2u.y = step_pos;
} else {
return -1;
}
if (move_step < 0)
if (index == MOTOR_AB)
motor_start(index, (move_step * -1), X_MOTOR_FORWARD);
else
motor_start(index, (move_step * -1), Y_MOTOR_FORWARD);
else
if (index == MOTOR_AB)
motor_start(index, move_step, X_MOTOR_BACKWARD);
else
motor_start(index, move_step, Y_MOTOR_BACKWARD);
return 0;
}
#endif
static int xy_motor_enable_by_time(int index, int set_dir, unsigned int ms)
{
struct motor_status *motor_status;
#if 0
if (!motorPwmPev->init_flag) {
pr_err("motor position was not init \n");
return -1;
}
#endif
if (index == MOTOR_AB)
motor_status = &motor_status_ab;
else if (index == MOTOR_CD)
motor_status = &motor_status_cd;
else
return -1;
#if 0
// MOTOR_DEBUG("set before x:%d, y:%d \n", ptz_ctrl_k2u.x,
//ptz_ctrl_k2u.y);
/* x/y limit checking */
// if (motor_status->index == MOTOR_AB) {
// /* x already in limit positions*/
// if(!gpio_get_value_cansleep(motor_pwm_dev->x_limit_gpio))
//{
// /*x = 0 limit*/
// if (ptz_ctrl_k2u.x < 3 && set_dir ==
//X_MOTOR_FORWARD)
// return 0;
// /*x = max limit*/
// else if (set_dir == X_MOTOR_BACKWARD)
// return 0;
// }
// } else {
// /*y has not limit detect, so checking y val whether in
//limit position*/
// if (ptz_ctrl_k2u.y <= 0 && set_dir == Y_MOTOR_BACKWARD)
// return 0;
// else if (ptz_ctrl_k2u.y >= motor_status->max_step &&
// set_dir ==
//Y_MOTOR_FORWARD)
// return 0;
// }
#endif
MOTOR_DEBUG("dir %d ,timer set ms:%d \n", set_dir, ms);
/*checking is running?*/
if (!motor_status->is_running) {
/*start timer*/
hrtimer_start(&motor_status->timer, ms_to_ktime(ms),
HRTIMER_MODE_REL);
/* get start time & update motor status*/
do_gettimeofday(&motor_status->start_time);
motor_status->direction = set_dir;
motor_status->is_running = 1;
/*let motor move ,and stop when time out*/
motor_start(motor_status->index, motor_status->max_step,
set_dir, motor_speed_table[motor_status->speed]);
} else {
/*the motor is running, so need to change*/
if (motor_status->direction == set_dir) {
/*cancle timer*/
hrtimer_cancel(&motor_status->timer);
/* restart timer */
hrtimer_start(&motor_status->timer, ms_to_ktime(ms),
HRTIMER_MODE_REL);
} else { /*need to change dir*/
/*stop and get now position*/
xy_motor_stop_and_get_position(motor_status->index);
/*start timer*/
hrtimer_start(&motor_status->timer, ms_to_ktime(ms),
HRTIMER_MODE_REL);
/* get start time & update motor status*/
do_gettimeofday(&motor_status->start_time);
motor_status->direction = set_dir;
motor_status->is_running = 1;
/*let motor move ,and stop when time out*/
motor_start(motor_status->index, motor_status->max_step,
set_dir,
motor_speed_table[motor_status->speed]);
}
}
#ifdef GPIO_LIMIT_DET
/*To try the motor was not move when start motor.*/
if (motor_status->index == MOTOR_AB) {
/* x already in limit positions*/
if (!gpio_get_value_cansleep(motor_pwm_dev->x_limit_gpio)) {
/*wait 10 step, and try the limit det again*/
msleep(motor_speed_table[motor_status->speed] * 10);
if (!gpio_get_value_cansleep(
motor_pwm_dev->x_limit_gpio))
/*motor can not move, because it was in limit
* position*/
xy_motor_stop_and_get_position(
motor_status->index);
}
}
#endif
return 0;
}
static int xy_motor_speed_set(int index, int speed)
{
struct motor_status *motor_status;
if (index == MOTOR_AB)
motor_status = &motor_status_ab;
else if (index == MOTOR_CD)
motor_status = &motor_status_cd;
else
return -1;
if (speed >= SPEED_NUM_MAX) {
pr_err("not support the speed \n");
return -1;
}
/*first stop motor*/
xy_motor_stop_and_get_position(motor_status->index);
MOTOR_DEBUG("set %d motor speed %d \n", index, speed);
motor_status->speed = speed;
return 0;
}
static void xy_init_work(struct work_struct *work)
{
unsigned int time;
struct motor_status *status =
container_of(work, struct motor_status, init_work);
/*motor fast move to x or y = 0*/
xy_motor_speed_set(status->index, 0);
if (status->index == MOTOR_AB) {
motor_start(status->index, status->max_step, X_MOTOR_BACKWARD,
motor_speed_table[status->speed]);
#ifdef GPIO_LIMIT_DET
/*first run a moment*/
msleep(motor_speed_table[status->speed] * 2 / 1000);
/* let motor_ab touch limit detectiong*/
MOTOR_DEBUG(
"gpio val: %d \n",
gpio_get_value_cansleep(motor_pwm_dev->x_limit_gpio));
while (gpio_get_value_cansleep(motor_pwm_dev->x_limit_gpio))
msleep(1);
#else
/* motor_cd has not gpio to detect, so will go all step*/
time = status->max_step * motor_speed_table[motor_status_ab.speed] / 1000;
MOTOR_DEBUG("x need to %d ms time to init \n", time);
msleep(time);
#endif
motor_pwm_dev->x_init = 1;
ptz_ctrl_k2u.x = 0;
MOTOR_DEBUG("x_work finish\n");
} else {
motor_start(status->index, status->max_step, Y_MOTOR_BACKWARD,
motor_speed_table[status->speed]);
/* motor_cd has not gpio to detect, so will go all step*/
time = status->max_step * motor_speed_table[motor_status_cd.speed] / 1000;
xy_motor_enable_by_time(status->index, Y_MOTOR_BACKWARD, time);
MOTOR_DEBUG("y need to %d ms time to init \n", time);
msleep(time);
motor_pwm_dev->y_init = 1;
ptz_ctrl_k2u.y = 0;
MOTOR_DEBUG("y_work finish\n");
}
/* if x and y are both init, will set init_flag*/
if (motor_pwm_dev->x_init && motor_pwm_dev->y_init)
motor_pwm_dev->init_flag = 1;
}
#ifdef GPIO_LIMIT_DET
static irqreturn_t motor_gpio_irq(int irq, void *dev_id)
{
MOTOR_DEBUG("into gpio irq \n");
/*stop the motor and get position*/
xy_motor_stop_and_get_position(MOTOR_AB);
return IRQ_HANDLED;
}
#endif
static void motor_irq_wrok(struct work_struct *work)
{
struct motor_status *status =
container_of(work, struct motor_status, motor_work);
MOTOR_DEBUG("motor%d into irq work \n", status->index);
/*stop the motor and get position*/
xy_motor_stop_and_get_position(status->index);
}
enum hrtimer_restart motor_hrtimer_func(struct hrtimer *timer)
{
struct motor_status *status =
container_of(timer, struct motor_status, timer);
schedule_work(&status->motor_work);
return HRTIMER_NORESTART;
}
static long ssp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int __user *argp = (unsigned int __user *)arg;
int set_dir;
MOTOR_DEBUG("into ioctrl \n");
switch (cmd) {
case SSP_MOTOR_POSITION_GET:
/* ensure xy is within the safe area */
if (ptz_ctrl_k2u.x > AB_STEPS) {
ptz_ctrl_k2u.x = AB_STEPS;
} else if (ptz_ctrl_k2u.x < 0) {
ptz_ctrl_k2u.x = 0;
}
if (ptz_ctrl_k2u.y > CD_STEPS) {
ptz_ctrl_k2u.y = CD_STEPS;
} else if (ptz_ctrl_k2u.y < 0) {
ptz_ctrl_k2u.y = 0;
}
if (copy_to_user(argp, &ptz_ctrl_k2u,
sizeof(struct pzt_ctrl))) {
pr_err("ssp---copy ptz_ctrl to user args failed!\n");
return -EFAULT;
}
break;
case SSP_MOTOR_RUNNED_TIME_GET:
if (copy_to_user(argp, &ptz_ctrl_k2u,
sizeof(struct pzt_ctrl))) {
pr_err("ssp---copy ptz_ctrl to user args failed!\n");
return -EFAULT;
}
ptz_ctrl_k2u.motor_runned_time[0] = 0;
ptz_ctrl_k2u.motor_runned_time[1] = 0;
break;
case SSP_DIR_SPEED_SET:
MOTOR_DEBUG("%d motor move by time \n", ptz_ctrl_u2k.dir);
if (copy_from_user(&ptz_ctrl_u2k, argp,
sizeof(struct pzt_ctrl))) {
pr_err("ssp---copy ptz_ctrl args from user failed!\n");
return -EFAULT;
}
if (ptz_ctrl_u2k.dir == 0)
break;
if (ptz_ctrl_u2k.run_time == 0) {
if ((DIR_RIGHT == ptz_ctrl_u2k.dir) ||
(DIR_LEFT == ptz_ctrl_u2k.dir)) {
ptz_ctrl_u2k.run_time =
AB_STEPS *
motor_speed_table[motor_status_ab.speed] / 1000;
} else {
ptz_ctrl_u2k.run_time =
CD_STEPS *
motor_speed_table[motor_status_cd.speed] / 1000;
}
/* set speed to slow */
ptz_ctrl_u2k.speed = 10;
}
if (ptz_ctrl_u2k.speed > SPEED_NUM_MAX)
ptz_ctrl_u2k.speed = SPEED_NUM_MAX - 1;
MOTOR_DEBUG("set speed : %d \n", ptz_ctrl_u2k.speed);
/*which motor ?*/
if (ptz_ctrl_u2k.dir > 0 && ptz_ctrl_u2k.dir < 3) { /*motor_cd*/
motor_status_cd.speed = ptz_ctrl_u2k.speed;
if (ptz_ctrl_u2k.dir == 1)
set_dir = Y_MOTOR_BACKWARD;
else
set_dir = Y_MOTOR_FORWARD;
/*enable motor start by time*/
xy_motor_enable_by_time(MOTOR_CD, set_dir,
ptz_ctrl_u2k.run_time);
} else if (ptz_ctrl_u2k.dir > 2) { /*motor_ab*/
motor_status_ab.speed = ptz_ctrl_u2k.speed;
if (ptz_ctrl_u2k.dir == 3)
set_dir = X_MOTOR_FORWARD;
else
set_dir = X_MOTOR_BACKWARD;
/*enable motor start by time*/
xy_motor_enable_by_time(MOTOR_AB, set_dir,
ptz_ctrl_u2k.run_time);
}
break;
case SSP_MOTOR_STOP:
ptz_ctrl_u2k.run_time = 0;
MOTOR_DEBUG("stop morot \n");
xy_motor_stop_and_get_position(MOTOR_AB);
xy_motor_stop_and_get_position(MOTOR_CD);
break;
case SSP_MOTOR_STEP_SPEED_SET:
if (copy_from_user(&ptz_ctrl_u2k, argp,
sizeof(struct pzt_ctrl))) {
pr_err("ssp---copy ptz_ctrl args from user failed!\n");
return -EFAULT;
}
xy_motor_speed_set(MOTOR_AB, ptz_ctrl_u2k.speed);
xy_motor_speed_set(MOTOR_CD, ptz_ctrl_u2k.speed);
break;
default:
pr_err("ssp---ssp_ioctl, No such ssp command %#x!\n", cmd);
return -1;
}
MOTOR_DEBUG("finish motor ioctrl \n");
return 0;
}
static int ssp_open(struct inode *inode, struct file *file)
{
MOTOR_DEBUG("now, init the motor\n");
memset(&ptz_ctrl_k2u, 0, sizeof(struct pzt_ctrl));
xy_motor_position_init();
return 0;
}
static int ssp_close(struct inode *inode, struct file *file)
{
return 0;
}
struct file_operations ssp_fops = {.owner = THIS_MODULE,
.unlocked_ioctl = ssp_ioctl,
.open = ssp_open,
.release = ssp_close};
static struct miscdevice ssp_dev = {
.minor = MISC_DYNAMIC_MINOR, .name = "ssp", .fops = &ssp_fops,
};
static int xy_motor_driver_probe(struct platform_device *pdev)
{
int ret;
struct motor_pwm_dev *motor_dev;
motor_dev = kzalloc(sizeof(struct motor_pwm_dev), GFP_KERNEL);
if (!motor_dev)
return -ENOMEM;
motor_pwm_dev = motor_dev;
platform_set_drvdata(pdev, motor_dev);
/* set init status to null*/
motor_dev->x_init = 0;
motor_dev->y_init = 0;
motor_dev->init_flag = 0;
motor_dev->ab = &motor_status_ab;
motor_dev->cd = &motor_status_cd;
#ifdef GPIO_LIMIT_DET
struct device_node *np = pdev->dev.of_node;
/* get and set gpio to input mode*/
motor_dev->x_limit_gpio = of_get_named_gpio(np, "xlimit-gpios", 0);
if (motor_dev->x_limit_gpio < 0) {
pr_err("can not get xlimit gpios");
}
if (!gpio_is_valid(motor_dev->x_limit_gpio)) {
pr_err("xlimit-gpios is invalid \n");
goto error;
}
if (devm_gpio_request(&pdev->dev, motor_dev->x_limit_gpio, "motor")) {
pr_err("failed to request gpio\n");
goto error;
}
gpio_direction_input(motor_dev->x_limit_gpio);
/*to request x irq*/
motor_pwm_dev->ab->irq = gpio_to_irq(motor_dev->x_limit_gpio);
ret = request_irq(motor_pwm_dev->ab->irq, motor_gpio_irq,
IRQF_TRIGGER_FALLING, "x_motor", &pdev->dev);
if (ret) {
pr_err("reques x motor erro \n");
goto error;
}
enable_irq_wake(motor_pwm_dev->ab->irq);
#endif
ret = misc_register(&ssp_dev);
if (0 != ret) {
pr_err("ssp---hi_ssp_init: register ssp_0 device failed! \n");
goto error;
}
/*init workqueue*/
INIT_WORK(&motor_pwm_dev->ab->init_work, xy_init_work);
INIT_WORK(&motor_pwm_dev->cd->init_work, xy_init_work);
INIT_WORK(&motor_pwm_dev->ab->motor_work, motor_irq_wrok);
INIT_WORK(&motor_pwm_dev->cd->motor_work, motor_irq_wrok);
/*init timer*/
hrtimer_init(&motor_status_ab.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
motor_status_ab.timer.function = motor_hrtimer_func;
hrtimer_init(&motor_status_cd.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
motor_status_cd.timer.function = motor_hrtimer_func;
pr_err("ssp---hi_ssp_init ok!\n");
return 0;
error:
kfree(motor_dev);
return -1;
}
static int xy_motor_driver_remove(struct platform_device *pdev)
{
int i;
printk("ssp---hi_ssp_exit \n");
/*free pwm*/
for (i = 0; i < 8; i++) {
if (pwm[i] != NULL) {
pwm_free(pwm[i]);
pwm[i] = NULL;
}
}
misc_deregister(&ssp_dev);
#ifdef GPIO_LIMIT_DET
free_irq(motor_pwm_dev->ab->irq, &pdev->dev);
gpio_free(motor_pwm_dev->x_limit_gpio);
#endif
cancel_work_sync(&motor_pwm_dev->ab->init_work);
cancel_work_sync(&motor_pwm_dev->cd->init_work);
cancel_work_sync(&motor_pwm_dev->ab->motor_work);
cancel_work_sync(&motor_pwm_dev->cd->motor_work);
kfree(motor_pwm_dev);
return 0;
}
static const struct of_device_id xy_pwm_motor_dt_ids[] = {
{.compatible = "xy,pwm_motor"},
{},
};
static struct platform_driver sunxi_led_driver = {
.probe = xy_motor_driver_probe,
.remove = xy_motor_driver_remove,
.driver = {
.name = "xy-pwm-motor",
.owner = THIS_MODULE,
.of_match_table = xy_pwm_motor_dt_ids,
},
};
module_platform_driver(sunxi_led_driver);
MODULE_AUTHOR("K.L <wuguanling@allwinnertech.com>");
MODULE_DESCRIPTION("PWM ctrl MOROT");
MODULE_LICENSE("GPL");