Add pwm helper api calls to control PWM using /sys/class entries. Following apis are supported: - pwm_chan_enable - pwm_chan_disable - pwm_chan_set_period - pwm_chan_set_duty_cycle - pwm_chan_create
Signed-off-by: Lokesh Vutla <lokeshvu...@ti.com> --- makefile | 2 +- pwm.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ pwm.h | 80 ++++++++++++++++++++ 3 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 pwm.c create mode 100644 pwm.h diff --git a/makefile b/makefile index a23945a..a965bd4 100644 --- a/makefile +++ b/makefile @@ -30,7 +30,7 @@ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o msg.o phc.o port.o \ port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) sk.o \ stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ - unicast_fsm.o unicast_service.o util.o version.o + unicast_fsm.o unicast_service.o util.o version.o pwm.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ sysoff.o timemaster.o diff --git a/pwm.c b/pwm.c new file mode 100644 index 0000000..0888092 --- /dev/null +++ b/pwm.c @@ -0,0 +1,223 @@ +/** + * @file pwm.c + * @note Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <ctype.h> +#include <fcntl.h> +#include <float.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "pwm.h" +#include "print.h" + +#define PWM_CHAN_EXPORT "/sys/class/pwm/pwmchip%d/export" +#define PWM_CHAN_UNEXPORT "/sys/class/pwm/pwmchip%d/unexport" +#define PWM_CHAN_ENABLE "/sys/class/pwm/pwmchip%d/pwm%d/enable" +#define PWM_CHAN_PERIOD "/sys/class/pwm/pwmchip%d/pwm%d/period" +#define PWM_CHAN_DUTY_CYCLE "/sys/class/pwm/pwmchip%d/pwm%d/duty_cycle" + +struct pwm_chan { + int pwm_id; + int chan_id; + int period_fd; + int dc_fd; +}; + +static int open_device(int perm, char const *dev, ...) +{ + char dev_buf[128]; + va_list ap; + int fd; + + va_start(ap, dev); + vsnprintf(dev_buf, sizeof(dev_buf), dev, ap); + va_end(ap); + + fd = open(dev_buf, perm); + if (fd < 0) { + pr_err("opening %s: Failed\n", dev_buf); + return fd; + } + + pr_debug("Opening %s: SUCCESS\n", dev_buf); + + return fd; +} + +static int pwm_chan_export(struct pwm_chan *chan) +{ + int pwm_fd, ret; + char buf[8]; + + pwm_fd = open_device(O_WRONLY, PWM_CHAN_EXPORT, chan->pwm_id); + if (pwm_fd < 0) + return pwm_fd; + + sprintf(buf, "%u",chan->chan_id); + ret = write(pwm_fd, buf, sizeof(buf)); + if (ret < 0) + pr_err("Exporting pwm %d channel %s: FAILED\n", + chan->pwm_id, buf); + else + pr_debug("Exporting pwm %d channel %d: SUCCESS\n", + chan->pwm_id, chan->chan_id); + + close(pwm_fd); + return (ret < 0) ? ret : 0; +} + +static int pwm_chan_unexport(struct pwm_chan *chan) +{ + int pwm_fd, ret; + char buf[8]; + + pwm_fd = open_device(O_WRONLY, PWM_CHAN_UNEXPORT, chan->pwm_id); + if (pwm_fd < 0) + return pwm_fd; + + sprintf(buf, "%u", chan->chan_id); + ret = write(pwm_fd, buf, sizeof(buf)); + if (ret < 0) + pr_err("Un-Exporting pwm %d channel %s: FAILED\n", + chan->pwm_id, buf); + + close(pwm_fd); + return (ret < 0) ? ret : 0; +} + +int pwm_chan_enable(struct pwm_chan *chan) +{ + int pwm_fd, ret; + + pwm_fd = open_device(O_WRONLY, PWM_CHAN_ENABLE, chan->pwm_id, + chan->chan_id); + if (pwm_fd < 0) + return pwm_fd; + + ret = write(pwm_fd, "1", 1); + if (ret < 0) + pr_err("Enabling pwm %d channel %d: FAILED\n", + chan->pwm_id, chan->chan_id); + else + pr_debug("Enabling pwm %d channel %d: SUCCESS\n", + chan->pwm_id, chan->chan_id); + + close(pwm_fd); + return (ret < 0) ? ret : 0; +} + +void pwm_chan_disable(struct pwm_chan *chan) +{ + int pwm_fd; + + pwm_fd = open_device(O_WRONLY, PWM_CHAN_ENABLE, chan->pwm_id, + chan->chan_id); + if (pwm_fd < 0) + return; + + if (write(pwm_fd, "0", 1) < 0) + pr_err("Disabling pwm %d channel %d: FAILED\n", + chan->pwm_id, chan->chan_id); + + close(pwm_fd); +} + +int pwm_chan_set_period(struct pwm_chan *chan, unsigned long long period) +{ + char dev_buf[128]; + int ret; + + snprintf(dev_buf, sizeof(dev_buf), "%llu", period); + ret = write(chan->period_fd, dev_buf, sizeof(dev_buf)); + if (ret < 0) + pr_err("Setting period %llu on pwm %d chan %d: FAILED\n", + period, chan->pwm_id, chan->chan_id); + else + pr_debug("Setting period %llu on pwm %d chan %d: SUCCESS\n", + period, chan->pwm_id, chan->chan_id); + + return (ret < 0) ? ret : 0; +} + +int pwm_chan_set_duty_cycle(struct pwm_chan *chan, unsigned long long dc) +{ + char dev_buf[128]; + int ret; + + snprintf(dev_buf, sizeof(dev_buf), "%llu", dc); + ret = write(chan->dc_fd, dev_buf, sizeof(dev_buf)); + if (ret < 0) + pr_err("Setting duty cycle %llu on chan %d pwm %d: FAILED\n", + dc, chan->pwm_id, chan->chan_id); + else + pr_debug("Setting duty cycle %llu on pwm %d chan %d: SUCCESS\n", + dc, chan->pwm_id, chan->chan_id); + + return (ret < 0) ? ret : 0; +} + +void pwm_chan_destroy(struct pwm_chan *chan) +{ + close(chan->dc_fd); + close(chan->period_fd); + pwm_chan_disable(chan); + pwm_chan_unexport(chan); + free(chan); +} + +struct pwm_chan *pwm_chan_create(int pwm_id, int chan_id) +{ + struct pwm_chan *chan; + + chan = calloc(1, sizeof(*chan)); + if (!chan) { + pr_err("Memory allocation for pwm chan: Failed\n"); + return NULL; + } + + chan->pwm_id = pwm_id; + chan->chan_id = chan_id; + + if (pwm_chan_export(chan)) + goto clean_chan; + + chan->period_fd = open_device(O_RDWR, PWM_CHAN_PERIOD, pwm_id, chan_id); + if (chan->period_fd < 0) + goto unexport_pwm; + + chan->dc_fd = open_device(O_RDWR, PWM_CHAN_DUTY_CYCLE, pwm_id, chan_id); + if (chan->dc_fd < 0) + goto close_period; + + return chan; + +close_period: + close(chan->period_fd); +unexport_pwm: + pwm_chan_unexport(chan); +clean_chan: + free(chan); + return NULL; +} diff --git a/pwm.h b/pwm.h new file mode 100644 index 0000000..0490d39 --- /dev/null +++ b/pwm.h @@ -0,0 +1,80 @@ +/** + * @file pwm.h + * @note Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_PWM_H +#define HAVE_PWM_H + +struct pwm_chan; + +/** + * Enable a PWM channel + * + * @param chan: The pwm channel to enable + * + * @return 0 if PWM channel is enabled + * else appropriate error value. + */ +int pwm_chan_enable(struct pwm_chan *chan); + +/** + * Disable a PWM channel + * + * @param chan: The pwm channel to disable + */ +void pwm_chan_disable(struct pwm_chan *chan); + +/** + * Set the period for a PWM channel + * + * @param chan: The pwm channel for period update + * @param period: New period of the pwm channel in ns + * + * @return 0 if PWM channel's period is updated + * else appropriate error value. + */ +int pwm_chan_set_period(struct pwm_chan *chan, unsigned long long period); + +/** + * Set the duty cycle for a PWM channel + * + * @param chan: The pwm channel for duty cycle update + * @param period: New duty cycle of the pwm channel in ns + * + * @return 0 if PWM channel's duty cycle is updated + * else appropriate error value. + */ +int pwm_chan_set_duty_cycle(struct pwm_chan *chan, unsigned long long dc); + +/** + * Destroy a PWM channel + * + * @param chan: The pwm channel to destroy + */ +void pwm_chan_destroy(struct pwm_chan *chan); + +/** + * Create a PWM channel + * + * @param pwm_id: PWM chip id of the pwm channel + * @param chan_id: PWM channel id + * + * @return Pointer to the pwm channel instance + */ +struct pwm_chan *pwm_chan_create(int pwm_id, int chan_id); + +#endif -- 2.23.0 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel