summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Souza <luiz@netgate.com>2017-06-20 15:07:17 -0500
committerLuiz Souza <luiz@netgate.com>2017-07-20 14:58:40 -0500
commit7901e9183df0f4c6cca2477a33a967d035d94c90 (patch)
tree4792805451fa9a86a70364cc1bf24484eb9568be
parentb7668aa23650dc6c1b806f5b940c73fc885ab793 (diff)
downloadFreeBSD-src-7901e9183df0f4c6cca2477a33a967d035d94c90.zip
FreeBSD-src-7901e9183df0f4c6cca2477a33a967d035d94c90.tar.gz
Add initial GPIO PWM support.
(cherry picked from commit a70ecde4228d0d6f81b0eb8117e102d01a9a36eb)
-rw-r--r--lib/libgpio/gpio.c41
-rw-r--r--lib/libgpio/libgpio.h8
-rw-r--r--sys/dev/gpio/gpio_if.m77
-rw-r--r--sys/dev/gpio/gpioc.c47
-rw-r--r--sys/sys/gpio.h21
-rw-r--r--usr.sbin/gpioctl/gpioctl.c66
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
*/
OpenPOWER on IntegriCloud