Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -269,6 +269,8 @@ proc_rwmem.9 \ pseudofs.9 \ psignal.9 \ + pwm.9 \ + pwmbus.9 \ random.9 \ random_harvest.9 \ ratecheck.9 \ Index: share/man/man9/pwm.9 =================================================================== --- /dev/null +++ share/man/man9/pwm.9 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2018 Emmanuel Vadot +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 12, 2018 +.Dt pwm 9 +.Os +.Sh NAME +.Nm pwm +.Nm PWM_GET_BUS , +.Nm PWM_CHANNEL_CONFIG , +.Nm PWM_CHANNEL_GET_CONFIG , +.Nm PWM_CHANNEL_SET_FLAGS , +.Nm PWM_CHANNEL_GET_FLAGS , +.Nm PWM_CHANNEL_ENABLE , +.Nm PWM_CHANNEL_IS_ENABLED , +.Nm PWM_CHANNEL_MAX , +.Sh SYNOPSIS +.Cd "device pwm" +.In "pwm_if.h" +.Ft device_t +.Fn PWM_GET_BUS "device_t dev" +.Ft int +.Fn PWM_CHANNEL_CONFIG "device_t dev" "int channel" "uint64_t period" "uint64_t duty" +.Ft int +.Fn PWM_CHANNEL_GET_CONFIG "device_t dev" "int channel" "uint64_t *period" "uint64_t *duty" +.Ft int +.Fn PWM_CHANNEL_SET_FLAGS "device_t dev" "int channel" "uint32_t flags" +.Ft int +.Fn PWM_CHANNEL_GET_FLAGS "device_t dev" "int channel" "uint32_t *flags" +.Ft int +.Fn PWM_CHANNEL_ENABLE "device_t dev" "int channel" "bool enable" +.Ft int +.Fn PWM_CHANNEL_IS_ENABLED "device_t dev" "int channel" "bool *enabled" +.Ft int +.Fn PWM_CHANNEL_MAX "device_t dev" "int channel" "int *nchannel" +.Sh DESCRIPTION +The PWM (Pulse-Width Modulation) interface allows the device drive to register to a global +bus so other devices in the kernel can use them in a generic way. +.Sh INTERFACE +.Bl -tag -width indent +.It Fn PWM_GET_BUS "device_t dev" +Return the bus device. +.It Fn PWM_CHANNEL_CONFIG "device_t dev" "int channel" "uint64_t period" "uint64_t duty" +Configure the period and duty (in nanoseconds) in the PWM controller for the specified channel. +Returns 0 on success or +.Er EINVAL +if the values are not supported by the controller or +.Er +EBUSY +is the PWM controller is in use and does not support changing the value on the fly. +.It Fn PWM_CHANNEL_GET_CONFIG "device_t dev" "int channel" "uint64_t *period" "uint64_t *duty" +Get the current configuration of the period and duty for the specified channel. +.It Fn PWM_CHANNEL_SET_FLAGS "device_t dev" "int channel" "uint32_t flags" +Set the flags of the channel (like inverted polarity). +.It Fn PWM_CHANNEL_GET_FLAGS "device_t dev" "int channel" "uint32_t *flags" +Get the current flags for the channel. +.It Fn PWM_CHANNEL_ENABLE "device_t dev" "int channel" "bool enable" +Enable the PWM channel. +.It Fn PWM_CHANNEL_ISENABLED "device_t dev" "int channel" "bool *enable" +Test if the PWM channel is enabled. +.It PWM_CHANNEL_MAX "device_t dev" "int channel" "int *nchannel" +Get the maximum number of channels supported by the controller. +.El +.Sh HISTORY +The pwm interface first appeared in +.Fx 13.0 . +The pwm interface and manual page was written by +.An Emmanuel Vadot Aq Mt manu@FreeBSD.org . Index: share/man/man9/pwmbus.9 =================================================================== --- /dev/null +++ share/man/man9/pwmbus.9 @@ -0,0 +1,88 @@ +.\" Copyright (c) 2018 Emmanuel Vadot +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 12, 2018 +.Dt pwmbus 9 +.Os +.Sh NAME +.Nm pwmbus +.Nm pwmbus_attach_bus , +.Nm PWMBUS_GET_BUS , +.Nm PWMBUS_CHANNEL_CONFIG , +.Nm PWMBUS_CHANNEL_GET_CONFIG , +.Nm PWMBUS_CHANNEL_SET_FLAGS , +.Nm PWMBUS_CHANNEL_GET_FLAGS , +.Nm PWMBUS_CHANNEL_ENABLE , +.Nm PWMBUS_CHANNEL_IS_ENABLED , +.Nm PWMBUS_CHANNEL_MAX , +.Sh SYNOPSIS +.Cd "device pwm" +.In "pwmbus_if.h" +.Ft device_t +.Fn pwmbus_attach_bus "device_t dev" +.Ft int +.Fn PWMBUS_CHANNEL_CONFIG "device_t bus" "int channel" "uint64_t period" "uint64_t duty" +.Ft int +.Fn PWMBUS_CHANNEL_GET_CONFIG "device_t bus" "int channel" "uint64_t *period" "uint64_t *duty" +.Ft int +.Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags" +.Ft int +.Fn PWMBUS_CHANNEL_GET_FLAGS "device_t bus" "int channel" "uint32_t *flags" +.Ft int +.Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable" +.Ft int +.Fn PWMBUS_CHANNEL_IS_ENABLED "device_t bus" "int channel" "bool *enabled" +.Ft int +.Fn PWMBUS_CHANNEL_MAX "device_t bus" "int channel" "int *nchannel" +.Sh DESCRIPTION +The PWMBUS (Pulse-Width Modulation) interface allow device driver to register to a global +bus so other device in the kernel can use them in a generic way +.Sh INTERFACE +.Bl -tag -width indent +.It Fn pwmbus_attach_bus "device_t dev" +Attach the pwmbus to the device driver +.It Fn PWMBUS_CHANNEL_CONFIG "device_t bus" "int channel" "uint64_t period" "uint64_t duty" +Configure the period and duty (in nanoseconds) in the pwm controller on the bus for the specified channel. +Returns 0 on success or EINVAL is the values are not supported by the controller or +EBUSY is the PWMBUS controller is in use and doesn't support changing the value on the fly. +.It Fn PWMBUS_CHANNEL_GET_CONFIG "device_t bus" "int channel" "uint64_t *period" "uint64_t *duty" +Get the current configuration of the period and duty for the specified channel. +.It Fn PWMBUS_CHANNEL_SET_FLAGS "device_t bus" "int channel" "uint32_t flags" +Set the flags of the channel (like inverted polarity), if the driver or controller +doesn't support this a default method is used. +.It Fn PWMBUS_CHANNEL_GET_FLAGS "device_t bus" "int channel" "uint32_t *flags" +Get the current flags for the channel, if the driver or controller +doesn't support this, a default method is used. +.It Fn PWMBUS_CHANNEL_ENABLE "device_t bus" "int channel" "bool enable" +Enable the pwm channel. +.It Fn PWMBUS_CHANNEL_ISENABLED "device_t bus" "int channel" "bool *enable" +Test if the pwm channel is enabled. +.It PWMBUS_CHANNEL_MAX "device_t bus" "int channel" "int *nchannel" +Get the maximum number of channel supported by the controller. +.El +.Sh HISTORY +The pwmbus interface first appear in +.Fx 13.0 . +The pwmbus interface and manual page was written by +.An Emmanuel Vadot Aq Mt manu@FreeBSD.org . Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2738,6 +2738,11 @@ dev/puc/puc_cfg.c optional puc dev/puc/puc_pccard.c optional puc pccard dev/puc/puc_pci.c optional puc pci +dev/pwm/pwmc.c optional pwm +dev/pwm/pwmbus.c optional pwm +dev/pwm/pwm_if.m optional pwm +dev/pwm/pwmbus_if.m optional pwm +dev/pwm/ofw_pwm.c optional pwm fdt dev/quicc/quicc_core.c optional quicc dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral Index: sys/dev/pwm/ofw_pwm.c =================================================================== --- /dev/null +++ sys/dev/pwm/ofw_pwm.c @@ -0,0 +1,109 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "pwm_if.h" + +int +pwm_get_by_ofw_propidx(device_t consumer, phandle_t node, + const char *prop_name, int idx, pwm_channel_t *out_channel) +{ + phandle_t xref; + pcell_t *cells; + struct pwm_channel channel; + int ncells, rv; + + rv = ofw_bus_parse_xref_list_alloc(node, prop_name, "#pwm-cells", + idx, &xref, &ncells, &cells); + if (rv != 0) + return (rv); + + channel.dev = OF_device_from_xref(xref); + if (channel.dev == NULL) { + OF_prop_free(cells); + return (ENODEV); + } + + channel.busdev = PWM_GET_BUS(channel.dev); + if (channel.busdev == NULL) { + OF_prop_free(cells); + return (ENODEV); + } + + channel.channel = cells[0]; + channel.period = cells[1]; + + if (ncells >= 3) + channel.flags = cells[2]; + + *out_channel = malloc(sizeof(struct pwm_channel), M_DEVBUF, M_WAITOK | M_ZERO); + **out_channel = channel; + return (0); +} + +int +pwm_get_by_ofw_idx(device_t consumer, phandle_t node, int idx, + pwm_channel_t *out_channel) +{ + + return (pwm_get_by_ofw_propidx(consumer, node, "pwms", idx, out_channel)); +} + +int +pwm_get_by_ofw_property(device_t consumer, phandle_t node, + const char *prop_name, pwm_channel_t *out_channel) +{ + + return (pwm_get_by_ofw_propidx(consumer, node, prop_name, 0, out_channel)); +} + +int +pwm_get_by_ofw_name(device_t consumer, phandle_t node, const char *name, + pwm_channel_t *out_channel) +{ + int rv, idx; + + rv = ofw_bus_find_string_index(node, "pwm-names", name, &idx); + if (rv != 0) + return (rv); + + return (pwm_get_by_ofw_idx(consumer, node, idx, out_channel)); +} Index: sys/dev/pwm/pwm_if.m =================================================================== --- /dev/null +++ sys/dev/pwm/pwm_if.m @@ -0,0 +1,106 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2018 Emmanuel Vadot +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +INTERFACE pwm; + +# +# Get the bus +# + +METHOD device_t get_bus { + device_t dev; +}; + +# +# Config the period (Total number of cycle in ns) and +# the duty (active number of cycle in ns) +# +METHOD int channel_config { + device_t dev; + int channel; + uint64_t period; + uint64_t duty; +}; + +# +# Get the period (Total number of cycle in ns) and +# the duty (active number of cycle in ns) +# +METHOD int channel_get_config { + device_t dev; + int channel; + uint64_t *period; + uint64_t *duty; +}; + +# +# Set the flags +# +METHOD int channel_set_flags { + device_t dev; + int channel; + uint32_t flags; +}; + +# +# Get the flags +# +METHOD int channel_get_flags { + device_t dev; + int channel; + uint32_t *flags; +}; + +# +# Enable the pwm output +# +METHOD int channel_enable { + device_t dev; + int channel; + bool enable; +}; + +# +# Is the pwm output enabled +# +METHOD int channel_is_enabled { + device_t dev; + int channel; + bool *enabled; +}; + +# +# Get the number of channels +# +METHOD int channel_max { + device_t dev; + int *nchannel; +}; Index: sys/dev/pwm/pwmbus.h =================================================================== --- /dev/null +++ sys/dev/pwm/pwmbus.h @@ -0,0 +1,62 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PWMBUS_H_ + +#include +#include + +struct pwm_channel { + device_t dev; + device_t busdev; + int channel; + uint64_t period; + uint64_t duty; + uint32_t flags; + bool enabled; +}; +typedef struct pwm_channel *pwm_channel_t; + +device_t pwmbus_attach_bus(device_t dev); +int pwmbus_acquire_channel(device_t bus, int channel); +int pwmbus_release_channel(device_t bus, int channel); + +int +pwm_get_by_ofw_propidx(device_t consumer, phandle_t node, + const char *prop_name, int idx, pwm_channel_t *channel); +int +pwm_get_by_ofw_idx(device_t consumer, phandle_t node, int idx, + pwm_channel_t *out_channel); +int +pwm_get_by_ofw_property(device_t consumer, phandle_t node, + const char *prop_name, pwm_channel_t *out_channel); +int +pwm_get_by_ofw_name(device_t consumer, phandle_t node, const char *name, + pwm_channel_t *out_channel); + +#endif /* _PWMBUS_H_ */ Index: sys/dev/pwm/pwmbus.c =================================================================== --- /dev/null +++ sys/dev/pwm/pwmbus.c @@ -0,0 +1,244 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pwmbus_if.h" +#include "pwm_if.h" + +struct pwmbus_channel_data { + int reserved; + char *name; +}; + +struct pwmbus_softc { + device_t busdev; + device_t dev; + + int nchannels; +}; + +device_t +pwmbus_attach_bus(device_t dev) +{ + device_t busdev; +#ifdef FDT + phandle_t node; +#endif + + busdev = device_add_child(dev, "pwmbus", -1); + if (busdev == NULL) { + device_printf(dev, "Cannot add child pwmbus\n"); + return (NULL); + } + if (device_add_child(dev, "pwmc", -1) == NULL) { + device_printf(dev, "Cannot add pwmc\n"); + device_delete_child(dev, busdev); + return (NULL); + } + +#ifdef FDT + node = ofw_bus_get_node(dev); + OF_device_register_xref(OF_xref_from_node(node), dev); +#endif + + bus_generic_attach(dev); + + return (busdev); +} + +static int +pwmbus_probe(device_t dev) +{ + + device_set_desc(dev, "PWM bus"); + return (BUS_PROBE_GENERIC); +} + +static int +pwmbus_attach(device_t dev) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(dev); + sc->busdev = dev; + sc->dev = device_get_parent(dev); + + if (PWM_CHANNEL_MAX(sc->dev, &sc->nchannels) != 0 || + sc->nchannels == 0) + return (ENXIO); + + if (bootverbose) + device_printf(dev, "Registering %d channel(s)\n", sc->nchannels); + bus_generic_probe(dev); + + return (bus_generic_attach(dev)); +} + +static int +pwmbus_detach(device_t dev) +{ + device_t *devlist; + int i, rv, ndevs; + + rv = bus_generic_detach(dev); + if (rv != 0) + return (rv); + + rv = device_get_children(dev, &devlist, &ndevs); + if (rv != 0) + return (rv); + for (i = 0; i < ndevs; i++) + device_delete_child(dev, devlist[i]); + + return (0); +} + +static int +pwmbus_channel_config(device_t bus, int channel, uint64_t period, uint64_t duty) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_CONFIG(sc->dev, channel, period, duty)); +} + +static int +pwmbus_channel_get_config(device_t bus, int channel, uint64_t *period, uint64_t *duty) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_GET_CONFIG(sc->dev, channel, period, duty)); +} + +static int +pwmbus_channel_set_flags(device_t bus, int channel, uint32_t flags) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_SET_FLAGS(sc->dev, channel, flags)); +} + +static int +pwmbus_channel_get_flags(device_t bus, int channel, uint32_t *flags) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_GET_FLAGS(sc->dev, channel, flags)); +} + +static int +pwmbus_channel_enable(device_t bus, int channel, bool enable) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_ENABLE(sc->dev, channel, enable)); +} + +static int +pwmbus_channel_is_enabled(device_t bus, int channel, bool *enable) +{ + struct pwmbus_softc *sc; + + sc = device_get_softc(bus); + + if (channel > sc->nchannels) + return (EINVAL); + + return (PWM_CHANNEL_IS_ENABLED(sc->dev, channel, enable)); +} + +static device_method_t pwmbus_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, pwmbus_probe), + DEVMETHOD(device_attach, pwmbus_attach), + DEVMETHOD(device_detach, pwmbus_detach), + + /* pwm interface */ + DEVMETHOD(pwmbus_channel_config, pwmbus_channel_config), + DEVMETHOD(pwmbus_channel_get_config, pwmbus_channel_get_config), + DEVMETHOD(pwmbus_channel_set_flags, pwmbus_channel_set_flags), + DEVMETHOD(pwmbus_channel_get_flags, pwmbus_channel_get_flags), + DEVMETHOD(pwmbus_channel_enable, pwmbus_channel_enable), + DEVMETHOD(pwmbus_channel_is_enabled, pwmbus_channel_is_enabled), + + DEVMETHOD_END +}; + +driver_t pwmbus_driver = { + "pwmbus", + pwmbus_methods, + sizeof(struct pwmbus_softc), +}; +devclass_t pwmbus_devclass; + +EARLY_DRIVER_MODULE(pwmbus, pwm, pwmbus_driver, pwmbus_devclass, 0, 0, + BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(pwmbus, 1); Index: sys/dev/pwm/pwmbus_if.m =================================================================== --- /dev/null +++ sys/dev/pwm/pwmbus_if.m @@ -0,0 +1,111 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2018 Emmanuel Vadot +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include + +INTERFACE pwmbus; + +CODE { + static int + pwm_default_set_flags(device_t dev, int channel, uint32_t flags) + { + + return (EOPNOTSUPP); + } + + static int + pwm_default_get_flags(device_t dev, int channel, uint32_t *flags) + { + + *flags = 0; + return (0); + } +}; + +HEADER { + #include +}; + +# +# Config the period (Total number of cycle in ns) and +# the duty (active number of cycle in ns) +# +METHOD int channel_config { + device_t bus; + int channel; + uint64_t period; + uint64_t duty; +}; + +# +# Get the period (Total number of cycle in ns) and +# the duty (active number of cycle in ns) +# +METHOD int channel_get_config { + device_t bus; + int channel; + uint64_t *period; + uint64_t *duty; +}; + +# +# Set the flags +# +METHOD int channel_set_flags { + device_t bus; + int channel; + uint32_t flags; +} DEFAULT pwm_default_set_flags; + +# +# Get the flags +# +METHOD int channel_get_flags { + device_t dev; + int channel; + uint32_t *flags; +} DEFAULT pwm_default_get_flags; + +# +# Enable the pwm output +# +METHOD int channel_enable { + device_t bus; + int channel; + bool enable; +}; + +# +# Is the pwm output enabled +# +METHOD int channel_is_enabled { + device_t bus; + int channel; + bool *enabled; +}; Index: sys/dev/pwm/pwmc.c =================================================================== --- /dev/null +++ sys/dev/pwm/pwmc.c @@ -0,0 +1,161 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pwmbus_if.h" +#include "pwm_if.h" + +struct pwmc_softc { + device_t dev; + device_t pdev; + struct cdev *pwm_dev; + char name[32]; +}; + +static int +pwm_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td) +{ + struct pwmc_softc *sc; + struct pwm_state state; + device_t bus; + int nchannel; + int rv = 0; + + sc = dev->si_drv1; + bus = PWM_GET_BUS(sc->pdev); + if (bus == NULL) + return (EINVAL); + + switch (cmd) { + case PWMMAXCHANNEL: + nchannel = -1; + rv = PWM_CHANNEL_MAX(sc->pdev, &nchannel); + bcopy(&nchannel, data, sizeof(nchannel)); + break; + case PWMSETSTATE: + bcopy(data, &state, sizeof(state)); + rv = PWMBUS_CHANNEL_CONFIG(bus, state.channel, + state.period, state.duty); + if (rv == 0) + rv = PWMBUS_CHANNEL_ENABLE(bus, state.channel, + state.enable); + break; + case PWMGETSTATE: + bcopy(data, &state, sizeof(state)); + rv = PWMBUS_CHANNEL_GET_CONFIG(bus, state.channel, + &state.period, &state.duty); + if (rv != 0) + return (rv); + rv = PWMBUS_CHANNEL_IS_ENABLED(bus, state.channel, + &state.enable); + if (rv != 0) + return (rv); + bcopy(&state, data, sizeof(state)); + break; + } + + return (rv); +} + +static struct cdevsw pwm_cdevsw = { + .d_version = D_VERSION, + .d_name = "pwm", + .d_ioctl = pwm_ioctl +}; + +static int +pwmc_probe(device_t dev) +{ + + device_set_desc(dev, "PWM Controller"); + return (0); +} + +static int +pwmc_attach(device_t dev) +{ + struct pwmc_softc *sc; + struct make_dev_args args; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->pdev = device_get_parent(dev); + + snprintf(sc->name, sizeof(sc->name), "pwmc%d", device_get_unit(dev)); + make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; + args.mda_devsw = &pwm_cdevsw; + args.mda_uid = UID_ROOT; + args.mda_gid = GID_OPERATOR; + args.mda_mode = 0600; + args.mda_si_drv1 = sc; + if (make_dev_s(&args, &sc->pwm_dev, "%s", sc->name) != 0) { + device_printf(dev, "Failed to make PWM device\n"); + return (ENXIO); + } + return (0); +} + +static int +pwmc_detach(device_t dev) +{ + + return (0); +} + +static device_method_t pwmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, pwmc_probe), + DEVMETHOD(device_attach, pwmc_attach), + DEVMETHOD(device_detach, pwmc_detach), + + DEVMETHOD_END +}; + +driver_t pwmc_driver = { + "pwmc", + pwmc_methods, + sizeof(struct pwmc_softc), +}; +devclass_t pwmc_devclass; + +DRIVER_MODULE(pwmc, pwm, pwmc_driver, pwmc_devclass, 0, 0); +MODULE_VERSION(pwmc, 1); Index: sys/sys/pwm.h =================================================================== --- /dev/null +++ sys/sys/pwm.h @@ -0,0 +1,51 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PWM_H_ +#define _PWM_H_ + +#define PWM_POLARITY_INVERTED (1 << 0) + +struct pwm_state { + int channel; + uint64_t period; + uint64_t duty; + uint32_t flags; + bool enable; +}; + +/* + * ioctls + */ + +#define PWMMAXCHANNEL _IOWR('G', 0, int) +#define PWMGETSTATE _IOWR('G', 1, struct pwm_state) +#define PWMSETSTATE _IOWR('G', 2, struct pwm_state) + + +#endif /* _PWM_H_ */ Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -67,6 +67,7 @@ pstat \ pw \ pwd_mkdb \ + pwm \ quot \ rarpd \ rmt \ Index: usr.sbin/pwm/Makefile =================================================================== --- /dev/null +++ usr.sbin/pwm/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= pwm +MAN= pwm.8 + +.include Index: usr.sbin/pwm/pwm.8 =================================================================== --- /dev/null +++ usr.sbin/pwm/pwm.8 @@ -0,0 +1,96 @@ +.\" Copyright (c) 2018 Emmanuel Vadot +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 12, 2018 +.Dt PWM 8 +.Os +.Sh NAME +.Nm pwm +.Nd configure pwm controller +.Sh SYNOPSIS +.Nm +.Op Fl f Ar device +.Fl c Ar channel +.Fl E +.Nm +.Op Fl f Ar device +.Fl c Ar channel +.Fl D +.Nm +.Op Fl f Ar device +.Fl c Ar channel +.Fl C +.Nm +.Op Fl f Ar device +.Fl c Ar channel +.Fl p period +.Nm +.Op Fl f Ar device +.Fl c Ar channel +.Fl d duty +.Sh DESCRIPTION +The +.Nm +utility can be used to configure pwm controllers. +.Pp +The options are as follow: +.Bl -tag -width ".Fl f Ar device" +.It Fl c Ar channel +Channel number to operate on +.It Fl E +Enable the pwm channel +.It Fl D +Disable the pwm channel +.It Fl C +Show the configuration of the pwm channel +.It Fl p Ar period +Configure the period (in nanoseconds) of the pwm channel +.It Fl d Ar duty +Configure the duty (in nanoseconds) of the pwm channel +.El +.Sh EXAMPLES +.Bl -bullet +.It +Show the configuration of the pwm channel: +.Pp +pwm -f /dev/pwmc0 -C +.It +Configure a 50000 ns period and a 25000 duty cycles: +.Pp +pwm -f /dev/pwmc0 -p 50000 -d 25000 +.El +.Sh SEE ALSO +.Xr pwm 9 , +.Xr pwmbus 9 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 13.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility and this manual page were written by +.An Emmanuel Vadot Aq Mt manu@FreeBSD.org . Index: usr.sbin/pwm/pwm.c =================================================================== --- /dev/null +++ usr.sbin/pwm/pwm.c @@ -0,0 +1,214 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define PWM_ENABLE 0x0001 +#define PWM_DISABLE 0x0002 +#define PWM_SHOW_CONFIG 0x0004 +#define PWM_PERIOD 0x0008 +#define PWM_DUTY 0x0010 + +static void +usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\tpwm [-f dev] -c channel -E\n"); + fprintf(stderr, "\tpwm [-f dev] -c channel -D\n"); + fprintf(stderr, "\tpwm [-f dev] -c channel -C\n"); + fprintf(stderr, "\tpwm [-f dev] -c channel -p period\n"); + fprintf(stderr, "\tpwm [-f dev] -c channel -d duty\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct pwm_state state; + int fd; + int channel, nchannels; + int period, duty; + int action, ch; +#ifdef HAVE_CAPSICUM + cap_rights_t right_ioctl; +#endif + + action = 0; + fd = -1; + channel = -1; + period = duty = -1; + + while ((ch = getopt(argc, argv, "f:c:EDCp:d:")) != -1) { + switch (ch) { + case 'E': + if (action) + usage(); + action = PWM_ENABLE; + break; + case 'D': + if (action) + usage(); + action = PWM_DISABLE; + break; + case 'C': + if (action) + usage(); + action = PWM_SHOW_CONFIG; + break; + case 'p': + if (action & ~(PWM_PERIOD | PWM_DUTY)) + usage(); + action = PWM_PERIOD; + period = strtol(optarg, NULL, 10); + break; + case 'd': + if (action & ~(PWM_PERIOD | PWM_DUTY)) + usage(); + action = PWM_DUTY; + duty = strtol(optarg, NULL, 10); + break; + case 'c': + if (channel != -1) + usage(); + channel = strtol(optarg, NULL, 10); + break; + case 'f': + if ((fd = open(optarg, O_RDWR)) < 0) { + fprintf(stderr, "pwm: cannot open %s %s\n", + optarg, strerror(errno)); + exit(1); + } + } + } + + if (fd == -1) { + if ((fd = open("/dev/pwmc0", O_RDWR)) < 0) { + fprintf(stderr, "pwm: cannot open %s %s\n", + optarg, strerror(errno)); + exit(1); + } + } + + if (action == 0 || fd == -1) + usage(); + +#ifdef HAVE_CAPSICUM + if (cap_enter() < 0) { + if (errno != ENOSYS) { + fprintf(stderr, "failed to enter capability mode\n"); + goto fail; + } + } else { + cap_right_init(&right_ioctl, CAP_IOCTL); + + if (cap_rights_limit(fd, &right_ioctl) < 0) { + fprintf(stderr, "cap_right_limit() failed\n"); + goto fail; + } + } +#endif + + /* Check if the channel is correct */ + if (ioctl(fd, PWMMAXCHANNEL, &nchannels) == -1) { + fprintf(stderr, "ioctl: %s\n", strerror(errno)); + goto fail; + } + if (channel > nchannels) { + fprintf(stderr, "pwm controller only support %d channels\n", + nchannels); + goto fail; + } + + /* Fill the common args */ + state.channel = channel; + if (ioctl(fd, PWMGETSTATE, &state) == -1) { + fprintf(stderr, "Cannot get current state of the pwm controller\n"); + goto fail; + } + + switch (action) { + case PWM_ENABLE: + if (state.enable == false) { + state.enable = true; + if (ioctl(fd, PWMSETSTATE, &state) == -1) { + fprintf(stderr, + "Cannot enable the pwm controller\n"); + goto fail; + } + } + break; + case PWM_DISABLE: + if (state.enable == true) { + state.enable = false; + if (ioctl(fd, PWMSETSTATE, &state) == -1) { + fprintf(stderr, + "Cannot disable the pwm controller\n"); + goto fail; + } + } + break; + case PWM_SHOW_CONFIG: + printf("period: %lu\nduty: %lu\nenabled:%d\n", + state.period, + state.duty, + state.enable); + break; + case PWM_PERIOD: + case PWM_DUTY: + if (period != -1) + state.period = period; + if (duty != -1) + state.duty = duty; + if (ioctl(fd, PWMSETSTATE, &state) == -1) { + fprintf(stderr, + "Cannot configure the pwm controller\n"); + goto fail; + } + break; + } + + close(fd); + + return (0); + +fail: + close(fd); + return (1); +}