diff options
author | Luiz Souza <luiz@netgate.com> | 2017-06-20 15:07:17 -0500 |
---|---|---|
committer | Luiz Souza <luiz@netgate.com> | 2017-07-20 14:58:40 -0500 |
commit | 7901e9183df0f4c6cca2477a33a967d035d94c90 (patch) | |
tree | 4792805451fa9a86a70364cc1bf24484eb9568be | |
parent | b7668aa23650dc6c1b806f5b940c73fc885ab793 (diff) | |
download | FreeBSD-src-7901e9183df0f4c6cca2477a33a967d035d94c90.zip FreeBSD-src-7901e9183df0f4c6cca2477a33a967d035d94c90.tar.gz |
Add initial GPIO PWM support.
(cherry picked from commit a70ecde4228d0d6f81b0eb8117e102d01a9a36eb)
-rw-r--r-- | lib/libgpio/gpio.c | 41 | ||||
-rw-r--r-- | lib/libgpio/libgpio.h | 8 | ||||
-rw-r--r-- | sys/dev/gpio/gpio_if.m | 77 | ||||
-rw-r--r-- | sys/dev/gpio/gpioc.c | 47 | ||||
-rw-r--r-- | sys/sys/gpio.h | 21 | ||||
-rw-r--r-- | usr.sbin/gpioctl/gpioctl.c | 66 |
6 files changed, 245 insertions, 15 deletions
diff --git a/lib/libgpio/gpio.c b/lib/libgpio/gpio.c index 8170822..48642f5 100644 --- a/lib/libgpio/gpio.c +++ b/lib/libgpio/gpio.c @@ -276,3 +276,44 @@ gpio_pin_pulsate(gpio_handle_t handle, gpio_pin_t pin) { return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULSATE)); } + +int +gpio_pin_pwm(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PWM)); +} + +int +gpio_pwm_get(gpio_handle_t handle, gpio_pwm_t pwm, gpio_pin_t pin, + uint32_t reg, uint32_t *value) +{ + struct gpio_pwm_req pwmreq; + + bzero(&pwmreq, sizeof(pwmreq)); + pwmreq.gp_pwm = pwm; + pwmreq.gp_pwm_pin = pin; + pwmreq.gp_pwm_reg = reg; + if (ioctl(handle, GPIOPWMGET, &pwmreq) < 0) + return (-1); + *value = pwmreq.gp_pwm_value; + + return (0); + +} + +int +gpio_pwm_set(gpio_handle_t handle, gpio_pwm_t pwm, gpio_pin_t pin, + uint32_t reg, uint32_t value) +{ + struct gpio_pwm_req pwmreq; + + bzero(&pwmreq, sizeof(pwmreq)); + pwmreq.gp_pwm = pwm; + pwmreq.gp_pwm_pin = pin; + pwmreq.gp_pwm_reg = reg; + pwmreq.gp_pwm_value = value; + if (ioctl(handle, GPIOPWMSET, &pwmreq) < 0) + return (-1); + + return (0); +} diff --git a/lib/libgpio/libgpio.h b/lib/libgpio/libgpio.h index a832234..6cc258e 100644 --- a/lib/libgpio/libgpio.h +++ b/lib/libgpio/libgpio.h @@ -35,6 +35,7 @@ __BEGIN_DECLS #define GPIO_INVALID_HANDLE -1 typedef int gpio_handle_t; +typedef int32_t gpio_pwm_t; typedef uint32_t gpio_pin_t; /* @@ -104,6 +105,13 @@ int gpio_pin_pulldown(gpio_handle_t, gpio_pin_t); int gpio_pin_invin(gpio_handle_t, gpio_pin_t); int gpio_pin_invout(gpio_handle_t, gpio_pin_t); int gpio_pin_pulsate(gpio_handle_t, gpio_pin_t); +int gpio_pin_pwm(gpio_handle_t, gpio_pin_t); + +/* PWM Settings. */ +int gpio_pwm_get(gpio_handle_t, gpio_pwm_t pwm, gpio_pin_t, + uint32_t, uint32_t *); +int gpio_pwm_set(gpio_handle_t, gpio_pwm_t pwm, gpio_pin_t, + uint32_t, uint32_t); __END_DECLS diff --git a/sys/dev/gpio/gpio_if.m b/sys/dev/gpio/gpio_if.m index 6306f33..d25a0c7 100644 --- a/sys/dev/gpio/gpio_if.m +++ b/sys/dev/gpio/gpio_if.m @@ -56,6 +56,43 @@ CODE { return (0); } + + static int + gpio_default_pwm_getcaps(device_t dev __unused, int32_t pwm __unused, + uint32_t pini __unused, uint32_t *caps) + { + + *caps = 0; + + return (0); + } + + static int + gpio_default_pwm_max(device_t dev __unused, uint32_t *pwmmax) + { + + *pwmmax = 0; + + return (0); + } + + static int + gpio_default_pwm_get(device_t dev __unused, uint32_t pwm __unused, + uint32_t pin __unused, uint32_t reg __unused, + uint32_t *value __unused) + { + + return (EINVAL); + } + + static int + gpio_default_pwm_set(device_t dev __unused, uint32_t pwm __unused, + uint32_t pin __unused, uint32_t reg __unused, + uint32_t value __unused) + { + + return (EINVAL); + } }; HEADER { @@ -140,6 +177,46 @@ METHOD int pin_setflags { }; # +# Get maximum pwm number +# +METHOD int pwm_max { + device_t dev; + int *maxpwm; +} DEFAULT gpio_default_pwm_max; + +# +# Get pwm capabilities +# +METHOD int pwm_getcaps { + device_t dev; + int32_t pwm_num; + uint32_t pin_num; + uint32_t *caps; +} DEFAULT gpio_default_pwm_getcaps; + +# +# Get pwm settings of pin specifed by pin_num +# +METHOD int pwm_get { + device_t dev; + int32_t pwm_num; + uint32_t pin_num; + uint32_t pwm_reg; + uint32_t *pwm_value; +} DEFAULT gpio_default_pwm_get; + +# +# Set pwm settings of pin specifed by pin_num +# +METHOD int pwm_set { + device_t dev; + int32_t pwm_num; + uint32_t pin_num; + uint32_t pwm_reg; + uint32_t pwm_value; +} DEFAULT gpio_default_pwm_set; + +# # Allow the GPIO controller to map the gpio-specifier on its own. # METHOD int map_gpios { diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c index a5a9d81..2a6895a 100644 --- a/sys/dev/gpio/gpioc.c +++ b/sys/dev/gpio/gpioc.c @@ -121,9 +121,10 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, struct thread *td) { device_t bus; - int max_pin, res; + int max_pin, max_pwm, res; struct gpioc_softc *sc = cdev->si_drv1; struct gpio_pin pin; + struct gpio_pwm_req pwmreq; struct gpio_req req; uint32_t caps; @@ -142,9 +143,16 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, &pin.gp_flags); /* Fail early */ - if (res) + if (res != 0) + break; + res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, + &pin.gp_caps); + if (res != 0) + break; + res = GPIO_PWM_GETCAPS(sc->sc_pdev, -1, pin.gp_pin, + &pin.gp_pwm_caps); + if (res != 0) break; - GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); bcopy(&pin, arg, sizeof(pin)); break; @@ -185,6 +193,39 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, pin.gp_name); break; + case GPIOMAXPWM: + max_pwm = -1; + res = GPIO_PWM_MAX(sc->sc_pdev, &max_pwm); + bcopy(&max_pwm, arg, sizeof(max_pwm)); + break; + case GPIOPWMGETCONFIG: + bcopy(arg, &pwmreq, sizeof(pwmreq)); + res = GPIO_PWM_GETCAPS(sc->sc_pdev, pwmreq.gp_pwm, + pwmreq.gp_pwm_pin, &pwmreq.gp_pwm_caps); + dprintf("pwm getcaps pwm %d pin %d -> caps %#x\n", + pwmreq.gp_pwm, pwmreq.gp_pwm_pin, + pwmreq.gp_pwm_caps); + bcopy(&pwmreq, arg, sizeof(pwmreq)); + break; + case GPIOPWMGET: + bcopy(arg, &pwmreq, sizeof(pwmreq)); + res = GPIO_PWM_GET(sc->sc_pdev, pwmreq.gp_pwm, + pwmreq.gp_pwm_pin, pwmreq.gp_pwm_reg, + &pwmreq.gp_pwm_value); + dprintf("pwm get pwm %d pin %d -> reg %#x %d\n", + pwmreq.gp_pwm, pwmreq.gp_pwm_pin, pwmreq.gp_pwm_reg, + pwmreq.gp_pwm_value); + bcopy(&pwmreq, arg, sizeof(pwmreq)); + break; + case GPIOPWMSET: + bcopy(arg, &pwmreq, sizeof(pwmreq)); + res = GPIO_PWM_SET(sc->sc_pdev, pwmreq.gp_pwm, + pwmreq.gp_pwm_pin, pwmreq.gp_pwm_reg, + pwmreq.gp_pwm_value); + dprintf("pwm set pwm %d pin %d -> reg %#x %d\n", + pwmreq.gp_pwm, pwmreq.gp_pwm_pin, pwmreq.gp_pwm_reg, + pwmreq.gp_pwm_value); + break; default: return (ENOTTY); break; diff --git a/sys/sys/gpio.h b/sys/sys/gpio.h index 9b0a1b5..4b63baa 100644 --- a/sys/sys/gpio.h +++ b/sys/sys/gpio.h @@ -56,6 +56,11 @@ #define GPIO_PIN_LOW 0x00 /* low level (logical 0) */ #define GPIO_PIN_HIGH 0x01 /* high level (logical 1) */ +/* GPIO PWM settings */ +#define GPIO_PWM_DUTY 0x01 /* PWM duty cycle */ +#define GPIO_PWM_FREQ 0x02 /* PWM frequency */ +#define GPIO_PWM_PERIOD 0x04 /* PWM period */ + /* Max name length of a pin */ #define GPIOMAXNAME 64 @@ -70,6 +75,8 @@ #define GPIO_PIN_INVIN 0x00000080 /* invert input */ #define GPIO_PIN_INVOUT 0x00000100 /* invert output */ #define GPIO_PIN_PULSATE 0x00000200 /* pulsate in hardware */ +#define GPIO_PIN_PWM 0x00000400 /* pwm output */ +#define GPIO_PIN_MUX 0x00000800 /* pin mux */ /* GPIO interrupt capabilities */ #define GPIO_INTR_NONE 0x00000000 /* no interrupt support */ #define GPIO_INTR_LEVEL_LOW 0x00010000 /* level trigger, low */ @@ -86,6 +93,7 @@ struct gpio_pin { char gp_name[GPIOMAXNAME]; /* human-readable name */ uint32_t gp_caps; /* capabilities */ uint32_t gp_flags; /* current flags */ + uint32_t gp_pwm_caps; /* pwm capabilities */ }; /* GPIO pin request (read/write/toggle) */ @@ -94,6 +102,15 @@ struct gpio_req { uint32_t gp_value; /* value */ }; +/* GPIO pwm request (read/write) */ +struct gpio_pwm_req { + int32_t gp_pwm; /* pwm number */ + uint32_t gp_pwm_pin; /* pin number */ + uint32_t gp_pwm_reg; /* register */ + uint32_t gp_pwm_value; /* value */ + uint32_t gp_pwm_caps; /* pwm capabilities */ +}; + /* * ioctls */ @@ -104,5 +121,9 @@ struct gpio_req { #define GPIOSET _IOW('G', 4, struct gpio_req) #define GPIOTOGGLE _IOWR('G', 5, struct gpio_req) #define GPIOSETNAME _IOW('G', 6, struct gpio_pin) +#define GPIOMAXPWM _IOR('G', 7, int) +#define GPIOPWMGETCONFIG _IOWR('G', 8, struct gpio_pwm_req) +#define GPIOPWMGET _IOWR('G', 9, struct gpio_pwm_req) +#define GPIOPWMSET _IOW('G', 10, struct gpio_pwm_req) #endif /* __GPIO_H__ */ diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c index f786ef6..21b2688 100644 --- a/usr.sbin/gpioctl/gpioctl.c +++ b/usr.sbin/gpioctl/gpioctl.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #define PIN_TYPE_NUMBER 1 #define PIN_TYPE_NAME 2 +#define PIN_TYPE_PWM 3 struct flag_desc { const char *name; @@ -60,10 +61,15 @@ static struct flag_desc gpio_flags[] = { { "II", GPIO_PIN_INVIN }, { "IO", GPIO_PIN_INVOUT }, { "PULSE", GPIO_PIN_PULSATE }, + { "PWM", GPIO_PIN_PWM }, { NULL, 0 }, }; -int str2cap(const char *str); +static struct flag_desc pwm_flags[] = { + { "DUTY", GPIO_PWM_DUTY }, + { "FREQ", GPIO_PWM_FREQ }, + { "PERIOD", GPIO_PWM_PERIOD }, +}; static void usage(void) @@ -73,7 +79,9 @@ usage(void) fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -t pin\n"); fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -c pin flag ...\n"); fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] -n pin pin-name\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] pin pwmflag [value]\n"); fprintf(stderr, "\tgpioctl [-f ctldev] [-pN] pin [0|1]\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -P pwm pwmflag [value]\n"); exit(1); } @@ -90,10 +98,9 @@ cap2str(uint32_t cap) return "UNKNOWN"; } -int -str2cap(const char *str) +static int +str2cap(const char *str, struct flag_desc *pdesc) { - struct flag_desc * pdesc = gpio_flags; while (pdesc->name) { if (strcasecmp(str, pdesc->name) == 0) return pdesc->flag; @@ -111,7 +118,7 @@ str2int(const char *s, int *ok) { char *endptr; int res = strtod(s, &endptr); - if (endptr != s + strlen(s) ) + if (endptr != s + strlen(s)) *ok = 0; else *ok = 1; @@ -211,13 +218,13 @@ main(int argc, char **argv) gpio_config_t pin; gpio_handle_t handle; char *ctlfile = NULL; - int pinn, pinv, pin_type, ch; + int pinn, pinv, pin_type, pwmn, ch; int flags, flag, ok; - int config, list, name, toggle, verbose; + int config, list, name, pwm, toggle, verbose; - config = toggle = verbose = list = name = pin_type = 0; + config = toggle = verbose = list = name = pin_type = pwm = 0; - while ((ch = getopt(argc, argv, "cf:lntvNp")) != -1) { + while ((ch = getopt(argc, argv, "cf:lntvNpP")) != -1) { switch (ch) { case 'c': config = 1; @@ -234,9 +241,12 @@ main(int argc, char **argv) case 'N': pin_type = PIN_TYPE_NAME; break; - case'p': + case 'p': pin_type = PIN_TYPE_NUMBER; break; + case 'P': + pin_type = PIN_TYPE_PWM; + break; case 't': toggle = 1; break; @@ -283,10 +293,13 @@ main(int argc, char **argv) fail("Can't find pin named \"%s\"\n", argv[0]); } break; + case PIN_TYPE_PWM: case PIN_TYPE_NUMBER: pinn = str2int(argv[0], &ok); if (!ok) - fail("Invalid pin number: %s\n", argv[0]); + fail("Invalid %s number: %s\n", + (pin_type == PIN_TYPE_NUMBER) ? "pin" : "pwm", + argv[0]); break; case PIN_TYPE_NAME: if ((pinn = get_pinnum_by_name(handle, argv[0])) == -1) @@ -322,7 +335,7 @@ main(int argc, char **argv) if (config) { flags = 0; for (i = 1; i < argc; i++) { - flag = str2cap(argv[i]); + flag = str2cap(argv[i], gpio_flags); if (flag < 0) fail("Invalid flag: %s\n", argv[i]); flags |= flag; @@ -336,6 +349,35 @@ main(int argc, char **argv) exit(0); } + /* PWM Settings */ + flags = -1; + if (argc > 0) + flags = str2cap(argv[1], pwm_flags); + if (flags != -1) { + if (pin_type == PIN_TYPE_PWM) { + pwmn = pinn; + pinn = -1; + } else + pwmn = -1; + if (argc > 2) { + /* Is it valid number ? */ + pinv = str2int(argv[2], &ok); + if (ok == 0) + fail("Invalid pin value: %s\n", argv[1]); + if (gpio_pwm_set(handle, pwmn, pinn, flags, pinv) < 0) { + perror("gpio_pwm_set"); + exit(1); + } + } else { + if (gpio_pwm_get(handle, pwmn, pinn, flags, &pinv) < 0) { + perror("gpio_pwm_get"); + exit(1); + } + printf("%d\n", pinv); + } + exit(0); + } + /* * Last two cases - set value or print value */ |