From 5221879ba48cef82126ed8febe07448f602afc7b Mon Sep 17 00:00:00 2001 From: carbon Date: Tue, 28 May 2024 17:26:44 +0800 Subject: [PATCH] add pwm api for milkv duo --- src/platform/milkv/duo.c | 5 + src/platform/platform.c | 4 + src/platform/platform.h | 5 +- src/soc/soc.h | 5 + src/soc/sophgo/cv180x.c | 224 +++++++++++++++++++++++++++++++++++++++ src/wiringx.c | 44 ++++++++ src/wiringx.h | 5 + 7 files changed, 291 insertions(+), 1 deletion(-) diff --git a/src/platform/milkv/duo.c b/src/platform/milkv/duo.c index 5dfad4a..2539947 100644 --- a/src/platform/milkv/duo.c +++ b/src/platform/milkv/duo.c @@ -54,4 +54,9 @@ void milkv_duoInit(void) { milkv_duo->setup = &milkv_duoSetup; milkv_duo->validGPIO = &milkv_duoValidGPIO; + + milkv_duo->pwmSetPeriod = milkv_duo->soc->socSetPWMPeriod; + milkv_duo->pwmSetDuty = milkv_duo->soc->socSetPWMDuty; + milkv_duo->pwmSetPolarity = milkv_duo->soc->socSetPWMPolarity; + milkv_duo->pwmEnable = milkv_duo->soc->socEnablePWM; } diff --git a/src/platform/platform.c b/src/platform/platform.c index 7694c34..1dfd9f1 100644 --- a/src/platform/platform.c +++ b/src/platform/platform.c @@ -42,6 +42,10 @@ void platform_register(struct platform_t **platform, char *name) { (*platform)->waitForInterrupt = NULL; (*platform)->isr = NULL; (*platform)->selectableFd = NULL; + (*platform)->pwmSetPeriod = NULL; + (*platform)->pwmSetDuty = NULL; + (*platform)->pwmSetPolarity = NULL; + (*platform)->pwmEnable = NULL; (*platform)->validGPIO = NULL; (*platform)->gc = NULL; diff --git a/src/platform/platform.h b/src/platform/platform.h index c1debb2..b3979a6 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -28,7 +28,10 @@ typedef struct platform_t { int (*waitForInterrupt)(int, int); int (*isr)(int, enum isr_mode_t); int (*selectableFd)(int); - + int (*pwmSetPeriod)(int, long); + int (*pwmSetDuty)(int, long); + int (*pwmSetPolarity)(int, int); + int (*pwmEnable)(int, int); int (*validGPIO)(int); int (*gc)(void); diff --git a/src/soc/soc.h b/src/soc/soc.h index 2c9572a..0ca65a2 100644 --- a/src/soc/soc.h +++ b/src/soc/soc.h @@ -50,6 +50,11 @@ typedef struct soc_t { void (*setIRQ)(int *, size_t size); char *(*getPinName)(int); + int (*socSetPWMPeriod)(int, long); + int (*socSetPWMDuty)(int, long); + int (*socSetPWMPolarity)(int, int); + int (*socEnablePWM)(int, int); + int (*validGPIO)(int); int (*selectableFd)(int); int (*gc)(void); diff --git a/src/soc/sophgo/cv180x.c b/src/soc/sophgo/cv180x.c index 5627075..8001594 100644 --- a/src/soc/sophgo/cv180x.c +++ b/src/soc/sophgo/cv180x.c @@ -13,6 +13,19 @@ #define CV180X_GPIO_GROUP_COUNT 4 +static int pin_pwm[12][2] = { + {2, 10}, // GP2 -> PWM10 + {3, 11}, // GP3 -> PWM11 + {4, 5}, // GP4 -> PWM5 + {5, 6}, // GP5 -> PWM6 + {6, 9}, // GP6 -> PWM9 + {7, 8}, // GP7 -> PWM8 + {8, 7}, // GP8 -> PWM7 + {9, 4}, // GP9 -> PWM4 + {12, 4}, // GP12 -> PWM4 + {13, 5} // GP13 -> PWM5 +}; + struct soc_t *cv180x = NULL; static struct layout_t { @@ -415,6 +428,212 @@ static int cv180xSelectableFd(int i) { return pin->fd; } +int cv180x_sysfs_pwm_set_long(struct soc_t *soc, char *path, long value) { + char out[16]; + int fd = 0; + if((fd = open(path, O_WRONLY)) <= 0) { + wiringXLog(LOG_ERR, "The %s %s cannot open %s for PWM (%s)", soc->brand, soc->chip, path, strerror(errno)); + return -1; + } + int l = snprintf(out, 16, "%ld", value); + if (write(fd, out, l) != l) { + wiringXLog(LOG_ERR, "The %s %s failed to write to %s for PWM (%s)", soc->brand, soc->chip, path, strerror(errno)); + close(fd); + return -1; + } + close(fd); + + return 0; +} + +int cv180x_sysfs_pwm_set_string(struct soc_t *soc, char *path, char *str) { + int fd = 0; + if ((fd = open(path, O_WRONLY)) <= 0) { + wiringXLog(LOG_ERR, "The %s %s cannot open %s for PWM (%s)", soc->brand, soc->chip, path, strerror(errno)); + return -1; + } + int l = strlen(str); + if (write(fd, str, l) != l) { + wiringXLog(LOG_ERR, "The %s %s failed to write to %s for PWM (%s)", soc->brand, soc->chip, path, strerror(errno)); + close(fd); + return -1; + } + close(fd); + + return 0; +} + +/* +index | 0 1 2 3 +----------+------------------------- +pwmchip0 -> pwm0, pwm1, pwm2, pwm3 +pwmchip4 -> pwm4, pwm5, pwm6, pwm7 +pwmchip8 -> pwm8, pwm9, pwm10,pwm11 +pwmchip12 -> pwm12,pwm13,pwm14,pwm15 +*/ +static int cv180x_get_pwm(int pin, int *chip, int *index) { + int i; + int found = 0; + int pwm; + + for (i = 0; i < 12; i++) { + if (pin == pin_pwm[i][0]) { + found = 1; + pwm = pin_pwm[i][1]; + break; + } + } + + if (found == 0) { + wiringXLog(LOG_ERR, "GP%d is not a PWM pin", pin); + return -1; + } + + if (pwm < 4 || pwm > 11) { + wiringXLog(LOG_ERR, "pwm %d not supported", pwm); + return -1; + } + + //wiringXLog(LOG_INFO, "GP%d is PWM%d", pin, pwm); + + *chip = (pwm / 4) * 4; + *index = pwm % 4; + + return pwm; +} + +static int cv180xSetPWMPeriod(int pin, long period) { + int chip = 0; + int index = 0; + char path[PATH_MAX]; + int pwm = cv180x_get_pwm(pin, &chip, &index); + + if (pwm < 0) { + wiringXLog(LOG_ERR, "[%s:%d] get pwm for pin(%d) failed!", __func__, __LINE__, pin); + return -1; + } + + memset(path, 0, sizeof(path)); + + //wiringXLog(LOG_INFO, "[%s:%d], GP%d/PWM%d(chip:%d,index:%d), period: %ld", __func__, __LINE__, pin, pwm, chip, index, period); + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d", chip, index); + if ((soc_sysfs_check_gpio(cv180x, path)) == -1) { + sprintf(path, "/sys/class/pwm/pwmchip%d/export", chip); + if (soc_sysfs_gpio_export(cv180x, path, index) == -1) { + return -1; + } + } + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d/period", chip, index); + if (cv180x_sysfs_pwm_set_long(cv180x, path, period) == -1) { + return -1; + } + + return 0; +} + +static int cv180xSetPWMDuty(int pin, long duty_cycle) { + int chip = 0; + int index = 0; + char path[PATH_MAX]; + int pwm = cv180x_get_pwm(pin, &chip, &index); + + if (pwm < 0) { + wiringXLog(LOG_ERR, "[%s:%d] get pwm for pin(%d) failed!", __func__, __LINE__, pin); + return -1; + } + + //wiringXLog(LOG_INFO, "[%s:%d], GP%d/PWM%d(chip:%d,index:%d), duty_cycle: %ld", __func__, __LINE__, pin, pwm, chip, index, duty_cycle); + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d", chip, index); + if ((soc_sysfs_check_gpio(cv180x, path)) == -1) { + sprintf(path, "/sys/class/pwm/pwmchip%d/export", chip); + if (soc_sysfs_gpio_export(cv180x, path, index) == -1) { + return -1; + } + } + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d/duty_cycle", chip, index); + if (cv180x_sysfs_pwm_set_long(cv180x, path, duty_cycle) == -1) { + return -1; + } + + return 0; +} + +/* + 0 - normal + 1 - inversed +*/ +static int cv180xSetPWMPolarity(int pin, int polarity) { + int chip = 0; + int index = 0; + char path[PATH_MAX]; + char polarity_str[16]; + int pwm = cv180x_get_pwm(pin, &chip, &index); + + if (pwm < 0) { + wiringXLog(LOG_ERR, "[%s:%d] get pwm for pin(%d) failed!", __func__, __LINE__, pin); + return -1; + } + + memset(path, 0, sizeof(path)); + + //wiringXLog(LOG_INFO, "[%s:%d], GP%d/PWM%d(chip:%d,index:%d), polarity: %ld", __func__, __LINE__, pin, pwm, chip, index, polarity); + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d", chip, index); + if ((soc_sysfs_check_gpio(cv180x, path)) == -1) { + sprintf(path, "/sys/class/pwm/pwmchip%d/export", chip); + if(soc_sysfs_gpio_export(cv180x, path, index) == -1) { + return -1; + } + } + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d/polarity", chip, index); + + if (polarity == 0) { + sprintf(polarity_str, "normal"); + } else { + sprintf(polarity_str, "inversed"); + } + + if (cv180x_sysfs_pwm_set_string(cv180x, path, polarity_str) == -1) { + return -1; + } + + return 0; +} + +static int cv180xEnablePWM(int pin, int enable) { + int chip = 0; + int index = 0; + char path[PATH_MAX]; + int pwm = cv180x_get_pwm(pin, &chip, &index); + + if (pwm < 0) { + wiringXLog(LOG_ERR, "[%s:%d] get pwm for pin(%d) failed!", __func__, __LINE__, pin); + return -1; + } + + //wiringXLog(LOG_INFO, "[%s:%d], GP%d/PWM%d(chip:%d,index:%d), enable: %ld", __func__, __LINE__, pin, pwm, chip, index, enable); + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d", chip, index); + if ((soc_sysfs_check_gpio(cv180x, path)) == -1) { + sprintf(path, "/sys/class/pwm/pwmchip%d/export", chip); + if (soc_sysfs_gpio_export(cv180x, path, index) == -1) { + return -1; + } + } + + sprintf(path, "/sys/class/pwm/pwmchip%d/pwm%d/enable", chip, index); + if (cv180x_sysfs_pwm_set_long(cv180x, path, enable) == -1) { + return -1; + } + + return 0; +} + void cv180xInit(void) { soc_register(&cv180x, "Sophgo", "CV180X"); @@ -435,4 +654,9 @@ void cv180xInit(void) { cv180x->setIRQ = &cv180xSetIRQ; cv180x->isr = &cv180xISR; cv180x->waitForInterrupt = &cv180xWaitForInterrupt; + + cv180x->socSetPWMPeriod = &cv180xSetPWMPeriod; + cv180x->socSetPWMDuty = &cv180xSetPWMDuty; + cv180x->socSetPWMPolarity = &cv180xSetPWMPolarity; + cv180x->socEnablePWM = &cv180xEnablePWM; } diff --git a/src/wiringx.c b/src/wiringx.c index eb7ebef..060b817 100644 --- a/src/wiringx.c +++ b/src/wiringx.c @@ -720,6 +720,50 @@ EXPORT int wiringXSerialGetChar(int fd) { } } +EXPORT int wiringXPWMSetPeriod(int pin, long period) { + if (platform == NULL) { + wiringXLog(LOG_ERR, "wiringX has not been properly setup (no platform has been selected)"); + return -1; + } else if (platform->pwmSetPeriod == NULL) { + wiringXLog(LOG_ERR, "The %s does not support the pwmSetPeriod functionality", platform->name[namenr]); + return -1; + } + return platform->pwmSetPeriod(pin, period); +} + +EXPORT int wiringXPWMSetDuty(int pin, long duty_cycle) { + if (platform == NULL) { + wiringXLog(LOG_ERR, "wiringX has not been properly setup (no platform has been selected)"); + return -1; + } else if (platform->pwmSetDuty == NULL) { + wiringXLog(LOG_ERR, "The %s does not support the pwmSetDuty functionality", platform->name[namenr]); + return -1; + } + return platform->pwmSetDuty(pin, duty_cycle); +} + +EXPORT int wiringXPWMSetPolarity(int pin, int polarity) { + if (platform == NULL) { + wiringXLog(LOG_ERR, "wiringX has not been properly setup (no platform has been selected)"); + return -1; + } else if (platform->pwmSetPolarity == NULL) { + wiringXLog(LOG_ERR, "The %s does not support the pwmSetPolarity functionality", platform->name[namenr]); + return -1; + } + return platform->pwmSetPolarity(pin, polarity); +} + +EXPORT int wiringXPWMEnable(int pin, int enable) { + if (platform == NULL) { + wiringXLog(LOG_ERR, "wiringX has not been properly setup (no platform has been selected)"); + return -1; + } else if (platform->pwmEnable == NULL) { + wiringXLog(LOG_ERR, "The %s does not support the pwmEnable functionality", platform->name[namenr]); + return -1; + } + return platform->pwmEnable(pin, enable); +} + EXPORT int wiringXSelectableFd(int gpio) { if(platform == NULL) { wiringXLog(LOG_ERR, "wiringX has not been properly setup (no platform has been selected)"); diff --git a/src/wiringx.h b/src/wiringx.h index 646ca80..649bc60 100644 --- a/src/wiringx.h +++ b/src/wiringx.h @@ -98,6 +98,11 @@ void wiringXSerialPrintf(int, const char *, ...); int wiringXSerialDataAvail(int); int wiringXSerialGetChar(int); +int wiringXPWMSetPeriod(int, long); +int wiringXPWMSetDuty(int, long); +int wiringXPWMSetPolarity(int, int); +int wiringXPWMEnable(int, int); + char *wiringXPlatform(void); int wiringXValidGPIO(int); int wiringXSelectableFd(int);