> Date: Sun, 6 Oct 2019 23:32:07 +0200
> From: Krystian Lewandowski <k.lewandow...@me.com>
> 
> Good evening Mark, tech@,
> I was trying to enable brightness control on Pinebook, so it could be set via:
> wsconsctl display.brightness=N%.
> 
> I added sxipwm and pwmbl drivers. First relies on "pwm" bindings,
> 2nd - on "pwm-backlight".
> 
> I had it working for some time now. For PWM, I tried to replicate SPI approach
> (spivar.h, pwm parent device, etc.).
> With latest pwm_... support all these are no longer necessary and I updated my
> sxipwm so pwm_... functions are now used.
> 
> I'm quite happy with sxipwm, pwmbl also does work for me but I didn't know how
> to properly use it in simplefb. You can see nasty approach in the patch.
> Do you have any suggestion how it should be done?
> 
> Device tree related parts:
> pwm: pwm@1c21400 {
>       compatible = "allwinner,sun50i-a64-pwm",
>               "allwinner,sun5i-a13-pwm";
>       reg = <0x01c21400 0x400>;
>       clocks = <&osc24M>;
>       pinctrl-names = "default";
>       pinctrl-0 = <&pwm_pin>;
>       #pwm-cells = <3>;
> [...]
> 
> pwm_pin: pwm_pin {
>       pins = "PD22";
>       function = "pwm";
> };
> 
> backlight: backlight {
>       compatible = "pwm-backlight";
>       pwms = <&pwm 0 50000 0>;
>       brightness-levels = <0 5 10 15 20 30 40 55 70 85 100>;
>       default-brightness-level = <2>;
>       enable-gpios = <&pio 3 23 GPIO_ACTIVE_HIGH>; /* PD23 */
>       power-supply = <&vdd_bl>;
> }

Cool.

The idea is that pwmbl(4) should implement pwmbl_get_param() and
pwmbl_set_para() functions and set the ws_get_param and ws_set_param
global function parameters like we do in dev/acpi/acpivout.c.  And
have simplefb(4) call these function pointers if they're set.

But before that can be done on arm64 those function pointers need to
be moved since dev/pci/vga_pci.c isn't included in the arm64 kernel.

I'll see if I can get that fixed.

> Index: sys/arch/arm64/conf/GENERIC
> ===================================================================
> RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
> retrieving revision 1.126
> diff -u -p -r1.126 GENERIC
> --- sys/arch/arm64/conf/GENERIC       30 Sep 2019 20:48:15 -0000      1.126
> +++ sys/arch/arm64/conf/GENERIC       6 Oct 2019 21:04:59 -0000
> @@ -105,6 +105,7 @@ option    WSDISPLAY_DEFAULTSCREENS=6      # init
>  
>  simplefb*    at fdt?
>  wsdisplay*   at simplefb?
> +pwmbl*                       at fdt?         # PWM backlight
>  
>  radeondrm*   at pci?
>  drm*         at radeondrm?
> @@ -203,6 +204,7 @@ sdmmc*            at sximmc?      # SD/MMC bus
>  sxisyscon*   at fdt? early 1 # System controller
>  sxitemp*     at fdt?         # Temperature sensor
>  sxitwi*              at fdt?         # I2C controller
> +sxipwm*              at fdt? # PWM controller
>  iic*         at sxitwi?      # I2C bus
>  dwxe*                at fdt?
>  
> Index: sys/dev/fdt/files.fdt
> ===================================================================
> RCS file: /cvs/src/sys/dev/fdt/files.fdt,v
> retrieving revision 1.95
> diff -u -p -r1.95 files.fdt
> --- sys/dev/fdt/files.fdt     30 Sep 2019 20:44:13 -0000      1.95
> +++ sys/dev/fdt/files.fdt     6 Oct 2019 21:05:05 -0000
> @@ -48,6 +48,14 @@ device     sxitwi: i2cbus
>  attach       sxitwi at fdt
>  file dev/fdt/sxitwi.c                sxitwi
>  
> +device       sxipwm
> +attach       sxipwm at fdt
> +file dev/fdt/sxipwm.c                sxipwm
> +
> +device       pwmbl
> +attach       pwmbl at fdt
> +file dev/fdt/pwmbl.c         pwmbl
> +
>  device       axppmic
>  attach       axppmic at i2c
>  attach       axppmic at rsb with axppmic_rsb
> @@ -205,7 +213,7 @@ attach    rkgpio at fdt
>  file dev/fdt/rkgpio.c                rkgpio
>  
>  device       rkiic: i2cbus
> -attach       rkiic at fdt                    
> +attach       rkiic at fdt
>  file dev/fdt/rkiic.c                 rkiic
>  
>  device       rkpmic
> Index: sys/dev/fdt/pwmbl.c
> ===================================================================
> RCS file: sys/dev/fdt/pwmbl.c
> diff -N sys/dev/fdt/pwmbl.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/dev/fdt/pwmbl.c       6 Oct 2019 21:05:07 -0000
> @@ -0,0 +1,219 @@
> +/*   $OpenBSD$       */
> +/*
> + * Copyright (c) 2019 Krystian Lewandowski
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/device.h>
> +#include <sys/malloc.h>
> +
> +#include <machine/fdt.h>
> +#include <machine/bus.h>
> +
> +#include <dev/ofw/openfirm.h>
> +#include <dev/ofw/ofw_gpio.h>
> +#include <dev/ofw/ofw_misc.h>
> +
> +#include <dev/wscons/wsconsio.h>
> +
> +struct pwmbl_softc {
> +     struct device           sc_dev;
> +     uint32_t                *sc_pwm;
> +     uint32_t                sc_period;
> +     uint32_t                sc_flags;
> +     uint32_t                sc_nlevels;
> +     uint32_t                *sc_levels;
> +     uint32_t                sc_def_level;
> +};
> +
> +extern int                   (*simplefb_read_cmd)(u_long, caddr_t);
> +extern int                   (*simplefb_write_cmd)(u_long, caddr_t);
> +
> +struct pwmbl_softc *sc_pwmbl;
> +int  pwmbl_match(struct device *, void *, void *);
> +void pwmbl_attach(struct device *, struct device *, void *);
> +int  pwmbl_get_brightness(void *, uint32_t *);
> +int  pwmbl_set_brightness(void *, uint32_t);
> +
> +int  pwmbl_simplefb_read_cmd(ulong, caddr_t);
> +int  pwmbl_simplefb_write_cmd(ulong, caddr_t);
> +
> +struct cfattach pwmbl_ca = {
> +     sizeof (struct pwmbl_softc), pwmbl_match, pwmbl_attach
> +};
> +
> +struct cfdriver pwmbl_cd = {
> +     NULL, "pwmbl", DV_DULL
> +};
> +
> +int
> +pwmbl_match(struct device *parent, void *match, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +
> +     return OF_is_compatible(faa->fa_node, "pwm-backlight");
> +}
> +
> +void
> +pwmbl_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
> +     struct fdt_attach_args *faa = aux;
> +
> +     uint32_t *gpios;
> +     int len;
> +
> +     len = OF_getproplen(faa->fa_node, "pwms");
> +     if (len <= 12) {
> +             printf(": no pwm\n");
> +             return;
> +     }
> +
> +     sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
> +     OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
> +
> +     sc->sc_period = sc->sc_pwm[2];
> +     if(len >= 16) {
> +             sc->sc_flags = sc->sc_pwm[3];
> +     } else {
> +             sc->sc_flags = 0;
> +     }
> +
> +     len = OF_getproplen(faa->fa_node, "enable-gpios");
> +     if (len < 0) {
> +             printf(": no gpio\n");
> +             return;
> +     }
> +
> +     gpios = malloc(len, M_TEMP, M_WAITOK);
> +     OF_getpropintarray(faa->fa_node, "enable-gpios", gpios, len);
> +     gpio_controller_config_pin(&gpios[0], GPIO_CONFIG_OUTPUT);
> +     gpio_controller_set_pin(&gpios[0], 1);
> +     free(gpios, M_TEMP, len);
> +
> +     len = OF_getproplen(faa->fa_node, "brightness-levels");
> +     if (len < 0) {
> +             printf(": no brightness levels\n");
> +             return;
> +     }
> +
> +     printf("\n");
> +
> +     sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
> +     OF_getpropintarray(faa->fa_node, "brightness-levels",
> +         sc->sc_levels, len);
> +     sc->sc_nlevels = len / sizeof(uint32_t);
> +
> +     sc->sc_def_level = OF_getpropint(faa->fa_node, 
> "default-brightness-level",
> +         sc->sc_levels[sc->sc_nlevels-1]);
> +
> +     // TODO: let simplefb to manage brightness via pwmbl
> +     sc_pwmbl = sc;
> +     simplefb_read_cmd = pwmbl_simplefb_read_cmd;
> +     simplefb_write_cmd = pwmbl_simplefb_write_cmd;
> +}
> +
> +int
> +pwmbl_get_brightness(void *cookie, uint32_t *level) {
> +     struct pwmbl_softc *sc = cookie;
> +     struct pwm_state ps;
> +     uint32_t bl_val;
> +     int i;
> +
> +     if (pwm_get_state(sc->sc_pwm, &ps))
> +             return EINVAL;
> +
> +     bl_val = (ps.ps_pulse_width * sc->sc_levels[sc->sc_nlevels - 1]) / 
> sc->sc_period;
> +
> +     for (i = 0; i < sc->sc_nlevels; i++) {
> +             if (bl_val <= sc->sc_levels[i]) {
> +                     *level = i;
> +                     break;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +int
> +pwmbl_set_brightness(void *cookie, uint32_t level) {
> +     struct pwmbl_softc *sc = cookie;
> +     struct pwm_state ps;
> +
> +     if (pwm_init_state(sc->sc_pwm, &ps))
> +             return EINVAL;
> +
> +     ps.ps_period = sc->sc_period;
> +     ps.ps_flags = sc->sc_flags;
> +     ps.ps_enabled = 1;
> +     ps.ps_pulse_width = (sc->sc_period * sc->sc_levels[level]) / 
> sc->sc_levels[sc->sc_nlevels - 1];
> +     return pwm_set_state(sc->sc_pwm, &ps);
> +}
> +
> +// TODO: find a better way for simplefb to manage brightness via pwmbl
> +int
> +pwmbl_simplefb_read_cmd(ulong cmd, caddr_t data)
> +{
> +     struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
> +     struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
> +     uint32_t level;
> +
> +     switch (cmd) {
> +     case WSDISPLAYIO_GETPARAM:
> +             switch (dp->param) {
> +             case WSDISPLAYIO_PARAM_BRIGHTNESS:
> +                     dp->min = sc->sc_levels[0];
> +                     dp->max = sc->sc_levels[sc->sc_nlevels - 1];
> +
> +                     if (pwmbl_get_brightness(sc, &level) >= 0) {
> +                             dp->curval = sc->sc_levels[level];
> +                             return 0;
> +                     }
> +                     break;
> +             }
> +             break;
> +     default:
> +             return -1;
> +     }
> +     return -1;
> +}
> +
> +int
> +pwmbl_simplefb_write_cmd(ulong cmd, caddr_t data)
> +{
> +     struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
> +     struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
> +     u_int i;
> +
> +     switch (cmd) {
> +     case WSDISPLAYIO_SETPARAM:
> +             switch (dp->param) {
> +             case WSDISPLAYIO_PARAM_BRIGHTNESS:
> +                     for (i = 0; i < sc->sc_nlevels; i++) {
> +                             if (dp->curval <= sc->sc_levels[i])
> +                                     break;
> +                     }
> +
> +                     if (pwmbl_set_brightness(sc, i) >= 0)
> +                             return 0;
> +                     break;
> +             }
> +             break;
> +     default:
> +             return -1;
> +     }
> +     return -1;
> +}
> Index: sys/dev/fdt/simplefb.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/fdt/simplefb.c,v
> retrieving revision 1.5
> diff -u -p -r1.5 simplefb.c
> --- sys/dev/fdt/simplefb.c    27 Aug 2018 09:30:07 -0000      1.5
> +++ sys/dev/fdt/simplefb.c    6 Oct 2019 21:05:10 -0000
> @@ -72,6 +72,9 @@ struct simplefb_softc {
>       psize_t                 sc_psize;
>  };
>  
> +int                  (*simplefb_read_cmd)(u_long, caddr_t);
> +int                  (*simplefb_write_cmd)(u_long, caddr_t);
> +
>  struct rasops_info simplefb_ri;
>  struct wsscreen_descr simplefb_wsd = { "std" };
>  struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
> @@ -230,9 +233,32 @@ int
>  simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
>  {
>       struct rasops_info *ri = v;
> +     struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
>       struct wsdisplay_fbinfo *wdf;
>  
>       switch (cmd) {
> +     case WSDISPLAYIO_GETPARAM:
> +             switch (dp->param) {
> +             case WSDISPLAYIO_PARAM_BRIGHTNESS:
> +                     if (simplefb_read_cmd) {
> +                             return simplefb_read_cmd(cmd, data);
> +                     }
> +                     break;
> +             default:
> +                     return -1;
> +             }
> +             break;
> +     case WSDISPLAYIO_SETPARAM:
> +             switch (dp->param) {
> +             case WSDISPLAYIO_PARAM_BRIGHTNESS:
> +                     if (simplefb_write_cmd) {
> +                             return simplefb_write_cmd(cmd, data);
> +                     }
> +                     break;
> +             default:
> +                     return -1;
> +             }
> +             break;
>       case WSDISPLAYIO_GTYPE:
>               *(int *)data = WSDISPLAY_TYPE_EFIFB;
>               return 0;
> Index: sys/dev/fdt/sxipwm.c
> ===================================================================
> RCS file: sys/dev/fdt/sxipwm.c
> diff -N sys/dev/fdt/sxipwm.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ sys/dev/fdt/sxipwm.c      6 Oct 2019 21:05:13 -0000
> @@ -0,0 +1,250 @@
> +/*   $OpenBSD$       */
> +/*
> + * Copyright (c) 2019 Krystian Lewandowski
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/device.h>
> +#include <sys/malloc.h>
> +
> +#include <machine/fdt.h>
> +#include <machine/bus.h>
> +
> +#include <dev/ofw/openfirm.h>
> +#include <dev/ofw/ofw_clock.h>
> +#include <dev/ofw/ofw_misc.h>
> +#include <dev/ofw/ofw_pinctrl.h>
> +#include <dev/ofw/fdt.h>
> +
> +#define PWM_CTRL_REG         0x0
> +#define PWM_CH0_PERIOD               0x4
> +#define PWM_CH0_PRESCAL      0xf
> +
> +#define PWM0_RDY             (1 << 28)
> +#define SCLK_CH0_GATING      (1 << 6)
> +#define PWM_CH0_ACT_STA      (1 << 5)
> +#define PWM_CH0_EN           (1 << 4)
> +
> +#define PWM_CH0_CYCLES_SHIFT         16
> +#define PWM_CH0_ACT_CYCLES_SHIFT     0
> +#define PWM_CH0_CYCLES_MAX           0xffff
> +
> +#define NS_PER_S             1000000000
> +
> +#define HREAD4(sc, reg)                                      \
> +(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
> +#define HWRITE4(sc, reg, val)                                        \
> +bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
> +
> +struct sxipwm_prescaler {
> +     uint32_t        divider;
> +     uint8_t mask;
> +};
> +
> +struct sxipwm_prescaler a64_prescalers[] = {
> +     { 1, 0xf },
> +     { 120, 0x0 },
> +     { 180, 0x1 },
> +     { 240, 0x2 },
> +     { 360, 0x3 },
> +     { 480, 0x4 },
> +     { 12000, 0x8 },
> +     { 24000, 0x9 },
> +     { 36000, 0xa },
> +     { 48000, 0xb },
> +     { 72000, 0xc },
> +     { 0 }
> +};
> +
> +struct sxipwm_softc {
> +     struct device           sc_dev;
> +     bus_space_tag_t sc_iot;
> +     bus_space_handle_t      sc_ioh;
> +
> +     struct sxipwm_prescaler *sc_prescalers;
> +     uint32_t                sc_clkin;
> +     struct pwm_device       sc_pd;
> +};
> +
> +int  sxipwm_match(struct device *, void *, void *);
> +void sxipwm_attach(struct device *, struct device *, void *);
> +int  sxipwm_get_state(void *, uint32_t *, struct pwm_state *);
> +int  sxipwm_set_state(void *, uint32_t *, struct pwm_state *);
> +
> +struct cfattach sxipwm_ca = {
> +     sizeof (struct sxipwm_softc), sxipwm_match, sxipwm_attach
> +};
> +
> +struct cfdriver sxipwm_cd = {
> +     NULL, "sxipwm", DV_DULL
> +};
> +
> +int
> +sxipwm_match(struct device *parent, void *match, void *aux)
> +{
> +     struct fdt_attach_args *faa = aux;
> +
> +     return OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-pwm");
> +}
> +
> +void
> +sxipwm_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct sxipwm_softc *sc = (struct sxipwm_softc *)self;
> +     struct fdt_attach_args *faa = aux;
> +
> +     if (faa->fa_nreg < 1) {
> +             printf(": no registers\n");
> +             return;
> +     }
> +
> +     sc->sc_clkin = clock_get_frequency_idx(faa->fa_node, 0);
> +     if (sc->sc_clkin == 0) {
> +             printf(": no clock\n");
> +             return;
> +     }
> +
> +     sc->sc_iot = faa->fa_iot;
> +     if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
> +         faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
> +             printf(": can't map registers\n");
> +             return;
> +     }
> +
> +     printf("\n");
> +
> +     pinctrl_byname(faa->fa_node, "default");
> +
> +     clock_enable_all(faa->fa_node);
> +     reset_deassert_all(faa->fa_node);
> +
> +     if (OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-pwm")) {
> +             sc->sc_prescalers = a64_prescalers;
> +     }
> +
> +     if (sc->sc_prescalers == NULL)
> +             return;
> +
> +     sc->sc_pd.pd_node = faa->fa_node;
> +     sc->sc_pd.pd_cookie = sc;
> +     sc->sc_pd.pd_get_state = sxipwm_get_state;
> +     sc->sc_pd.pd_set_state = sxipwm_set_state;
> +
> +     pwm_register(&sc->sc_pd);
> +}
> +
> +int
> +sxipwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
> +{
> +     struct sxipwm_softc *sc = cookie;
> +     uint32_t idx = cells[0];
> +     uint32_t ctrl_reg, ch_period_reg;
> +     uint16_t cycles, act_cycles;
> +     uint32_t rate;
> +     int i, prescaler;
> +
> +     if (idx != 0)
> +             return EINVAL;
> +
> +     ctrl_reg = HREAD4(sc, PWM_CTRL_REG);
> +     ch_period_reg = HREAD4(sc, PWM_CH0_PERIOD);
> +
> +     prescaler = -1;
> +     for (i = 0; sc->sc_prescalers[i].divider; i++) {
> +             if ((ctrl_reg & PWM_CH0_PRESCAL) == sc->sc_prescalers[i].mask) {
> +                     prescaler = i;
> +             }
> +     }
> +
> +     if (prescaler < 0)
> +             return EINVAL;
> +
> +     rate = sc->sc_clkin / sc->sc_prescalers[prescaler].divider;
> +     cycles = ((ch_period_reg >> PWM_CH0_CYCLES_SHIFT) + 1 ) & 
> PWM_CH0_CYCLES_MAX;
> +     act_cycles = (ch_period_reg >> PWM_CH0_ACT_CYCLES_SHIFT) & 
> PWM_CH0_CYCLES_MAX;
> +
> +     memset(ps, 0, sizeof(struct pwm_state));
> +     ps->ps_period = NS_PER_S / (rate / cycles);
> +     ps->ps_pulse_width = NS_PER_S / (rate / act_cycles);
> +     ps->ps_enabled = (ctrl_reg & (PWM_CH0_EN | SCLK_CH0_GATING)) == 
> (PWM_CH0_EN | SCLK_CH0_GATING);
> +
> +     return 0;
> +}
> +
> +int
> +sxipwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps)
> +{
> +     struct sxipwm_softc *sc = cookie;
> +     uint32_t idx = cells[0];
> +     uint32_t reg, period_freq, duty_freq, rate;
> +     uint16_t cycles, act_cycles;
> +     int i, prescaler;
> +
> +     period_freq = NS_PER_S / ps->ps_period;
> +     duty_freq = NS_PER_S / (ps->ps_pulse_width ? ps->ps_pulse_width : 1);
> +
> +     if (idx != 0)
> +             return EINVAL;
> +
> +     if (period_freq > sc->sc_clkin)
> +             return EINVAL;
> +
> +     if (duty_freq < period_freq)
> +             return EINVAL;
> +
> +     prescaler = -1;
> +     for (i = 0; sc->sc_prescalers[i].divider; i++) {
> +             rate = sc->sc_clkin / (period_freq * 
> sc->sc_prescalers[i].divider);
> +             if ((rate - 1) < PWM_CH0_CYCLES_MAX) {
> +                     prescaler = i;
> +                     break;
> +             }
> +     }
> +
> +     if (prescaler < 0)
> +             return EINVAL;
> +
> +     reg = HREAD4(sc, PWM_CTRL_REG);
> +
> +     if (reg & PWM0_RDY)
> +             return EBUSY;
> +
> +     if (ps->ps_enabled) {
> +             reg |= (PWM_CH0_EN | SCLK_CH0_GATING);
> +     } else {
> +             reg &= ~(PWM_CH0_EN | SCLK_CH0_GATING);
> +     }
> +
> +     if (ps->ps_flags & PWM_POLARITY_INVERTED) {
> +             reg &= ~PWM_CH0_ACT_STA;
> +     } else {
> +             reg |= PWM_CH0_ACT_STA;
> +     }
> +
> +     reg &= ~PWM_CH0_PRESCAL;
> +     reg |= sc->sc_prescalers[prescaler].mask;
> +     HWRITE4(sc, PWM_CTRL_REG, reg);
> +
> +     rate = sc->sc_clkin / sc->sc_prescalers[prescaler].divider;
> +     cycles = rate / period_freq;
> +     act_cycles = rate / duty_freq;
> +
> +     reg = ((cycles - 1) << PWM_CH0_CYCLES_SHIFT) |
> +         (act_cycles << PWM_CH0_ACT_CYCLES_SHIFT);
> +     HWRITE4(sc, PWM_CH0_PERIOD, reg);
> +
> +     return 0;
> +}
> 
> 

Reply via email to