| /* SPDX-License-Identifier: LGPL-2.1 */ |
| /* SPDX-FileCopyrightText: 2023 Uwe Kleine-König <u.kleine-koenig@pengutronix.de> */ |
| |
| #include "config.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/pwm.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| |
| #include <pwm.h> |
| |
| #include "pwm-internal.h" |
| |
| struct pwm_cdev { |
| struct pwm pwm; |
| unsigned int hwpwm; |
| }; |
| |
| struct pwm_chip_cdev { |
| struct pwm_chip chip; |
| int fd; |
| struct pwm_cdev *pwms[]; |
| }; |
| |
| static void pwm_chip_cdev_close(struct pwm_chip *chip) |
| { |
| struct pwm_chip_cdev *chip_cdev = container_of(chip, struct pwm_chip_cdev, chip); |
| |
| close(chip_cdev->fd); |
| free(chip_cdev); |
| } |
| |
| static struct pwm *pwm_chip_cdev_get_pwm(struct pwm_chip *chip, |
| unsigned int offset) |
| { |
| struct pwm_chip_cdev *chip_cdev = container_of(chip, struct pwm_chip_cdev, chip); |
| struct pwm_cdev *pwm_cdev; |
| struct pwm *pwm; |
| |
| if (chip_cdev->pwms[offset]) |
| return &chip_cdev->pwms[offset]->pwm; |
| |
| pwm_cdev = calloc(1, sizeof(*pwm_cdev)); |
| if (!pwm_cdev) |
| return NULL; |
| pwm = &pwm_cdev->pwm; |
| pwm->chip = chip; |
| pwm_cdev->hwpwm = offset; |
| |
| return pwm; |
| } |
| |
| static int pwm_chip_cdev_apply_state(struct pwm *pwm, |
| const struct pwm_state *state) |
| { |
| struct pwm_cdev *pwm_cdev = container_of(pwm, struct pwm_cdev, pwm); |
| struct pwm_chip_cdev *chip_cdev = container_of(pwm->chip, struct pwm_chip_cdev, chip); |
| const struct pwmchip_state s = { |
| .hwpwm = pwm_cdev->hwpwm, |
| .period = state->period, |
| .duty_cycle = state->duty_cycle, |
| .duty_offset = state->duty_offset, |
| }; |
| |
| return ioctl(chip_cdev->fd, PWM_IOCTL_APPLY, &s); |
| } |
| |
| static int pwm_chip_cdev_get_state(struct pwm *pwm, struct pwm_state *state) |
| { |
| errno = EIO; |
| return -1; |
| } |
| |
| struct pwm_chip *pwm_chip_cdev_open_by_number(unsigned int num) |
| { |
| struct pwm_chip_cdev *chip_cdev; |
| struct pwm_chip *chip; |
| int fd; |
| ssize_t ret; |
| unsigned long npwm; |
| char buf[128]; |
| |
| ret = snprintf(buf, sizeof(buf), "/dev/pwmchip%d", num); |
| if (ret < 0) |
| return NULL; |
| if (ret >= sizeof(buf)) { |
| /* huh */ |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| fd = open(buf, O_RDWR | O_CLOEXEC); |
| if (fd < 0) |
| return NULL; |
| |
| ret = ioctl(fd, PWM_IOCTL_GET_NUM_PWMS); |
| if (ret < 0) { |
| close(fd); |
| return NULL; |
| } |
| |
| npwm = ret; |
| |
| chip_cdev = calloc(1, sizeof(*chip_cdev) + npwm * sizeof(chip_cdev->pwms[0])); |
| if (!chip_cdev) { |
| close(fd); |
| return NULL; |
| } |
| |
| chip = &chip_cdev->chip; |
| chip->close = pwm_chip_cdev_close; |
| chip->get_pwm = pwm_chip_cdev_get_pwm; |
| chip->apply_state = pwm_chip_cdev_apply_state; |
| chip->get_state = pwm_chip_cdev_get_state; |
| chip->npwm = npwm; |
| |
| chip_cdev->fd = fd; |
| |
| return chip; |
| } |