102e91208SEmmanuel Vadot /*-
202e91208SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause
302e91208SEmmanuel Vadot *
402e91208SEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
502e91208SEmmanuel Vadot *
602e91208SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
702e91208SEmmanuel Vadot * modification, are permitted provided that the following conditions
802e91208SEmmanuel Vadot * are met:
902e91208SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
1002e91208SEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
1102e91208SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
1202e91208SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
1302e91208SEmmanuel Vadot * documentation and/or other materials provided with the distribution.
1402e91208SEmmanuel Vadot *
1502e91208SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1602e91208SEmmanuel Vadot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1702e91208SEmmanuel Vadot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1802e91208SEmmanuel Vadot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1902e91208SEmmanuel Vadot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2002e91208SEmmanuel Vadot * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2102e91208SEmmanuel Vadot * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2202e91208SEmmanuel Vadot * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2302e91208SEmmanuel Vadot * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402e91208SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502e91208SEmmanuel Vadot * SUCH DAMAGE.
2602e91208SEmmanuel Vadot */
2702e91208SEmmanuel Vadot
2802e91208SEmmanuel Vadot #include <sys/param.h>
2902e91208SEmmanuel Vadot #include <sys/systm.h>
3002e91208SEmmanuel Vadot #include <sys/bus.h>
3102e91208SEmmanuel Vadot #include <sys/kernel.h>
3202e91208SEmmanuel Vadot #include <sys/module.h>
3302e91208SEmmanuel Vadot #include <sys/rman.h>
3402e91208SEmmanuel Vadot #include <sys/resource.h>
3502e91208SEmmanuel Vadot #include <machine/bus.h>
3602e91208SEmmanuel Vadot
3702e91208SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
3802e91208SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
3902e91208SEmmanuel Vadot
40be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
4102e91208SEmmanuel Vadot
4202e91208SEmmanuel Vadot #include "pwmbus_if.h"
4302e91208SEmmanuel Vadot
4402e91208SEmmanuel Vadot #define AW_PWM_CTRL 0x00
4502e91208SEmmanuel Vadot #define AW_PWM_CTRL_PRESCALE_MASK 0xF
4602e91208SEmmanuel Vadot #define AW_PWM_CTRL_EN (1 << 4)
4702e91208SEmmanuel Vadot #define AW_PWM_CTRL_ACTIVE_LEVEL_HIGH (1 << 5)
4802e91208SEmmanuel Vadot #define AW_PWM_CTRL_GATE (1 << 6)
4902e91208SEmmanuel Vadot #define AW_PWM_CTRL_MODE_MASK 0x80
5002e91208SEmmanuel Vadot #define AW_PWM_CTRL_PULSE_MODE (1 << 7)
5102e91208SEmmanuel Vadot #define AW_PWM_CTRL_CYCLE_MODE (0 << 7)
5202e91208SEmmanuel Vadot #define AW_PWM_CTRL_PULSE_START (1 << 8)
5302e91208SEmmanuel Vadot #define AW_PWM_CTRL_CLK_BYPASS (1 << 9)
5402e91208SEmmanuel Vadot #define AW_PWM_CTRL_PERIOD_BUSY (1 << 28)
5502e91208SEmmanuel Vadot
5602e91208SEmmanuel Vadot #define AW_PWM_PERIOD 0x04
5702e91208SEmmanuel Vadot #define AW_PWM_PERIOD_TOTAL_MASK 0xFFFF
5802e91208SEmmanuel Vadot #define AW_PWM_PERIOD_TOTAL_SHIFT 16
5902e91208SEmmanuel Vadot #define AW_PWM_PERIOD_ACTIVE_MASK 0xFFFF
6002e91208SEmmanuel Vadot #define AW_PWM_PERIOD_ACTIVE_SHIFT 0
6102e91208SEmmanuel Vadot
6202e91208SEmmanuel Vadot #define AW_PWM_MAX_FREQ 24000000
6302e91208SEmmanuel Vadot
6402e91208SEmmanuel Vadot #define NS_PER_SEC 1000000000
6502e91208SEmmanuel Vadot
6602e91208SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
6702e91208SEmmanuel Vadot { "allwinner,sun5i-a13-pwm", 1 },
6802e91208SEmmanuel Vadot { "allwinner,sun8i-h3-pwm", 1 },
6902e91208SEmmanuel Vadot { NULL, 0 }
7002e91208SEmmanuel Vadot };
7102e91208SEmmanuel Vadot
7202e91208SEmmanuel Vadot static struct resource_spec aw_pwm_spec[] = {
7302e91208SEmmanuel Vadot { SYS_RES_MEMORY, 0, RF_ACTIVE },
7402e91208SEmmanuel Vadot { -1, 0 }
7502e91208SEmmanuel Vadot };
7602e91208SEmmanuel Vadot
7702e91208SEmmanuel Vadot struct aw_pwm_softc {
7802e91208SEmmanuel Vadot device_t dev;
7902e91208SEmmanuel Vadot device_t busdev;
8002e91208SEmmanuel Vadot clk_t clk;
8102e91208SEmmanuel Vadot struct resource *res;
8202e91208SEmmanuel Vadot
8302e91208SEmmanuel Vadot uint64_t clk_freq;
8402e91208SEmmanuel Vadot unsigned int period;
8502e91208SEmmanuel Vadot unsigned int duty;
8602e91208SEmmanuel Vadot uint32_t flags;
8702e91208SEmmanuel Vadot bool enabled;
8802e91208SEmmanuel Vadot };
8902e91208SEmmanuel Vadot
9002e91208SEmmanuel Vadot static uint32_t aw_pwm_clk_prescaler[] = {
9102e91208SEmmanuel Vadot 120,
9202e91208SEmmanuel Vadot 180,
9302e91208SEmmanuel Vadot 240,
9402e91208SEmmanuel Vadot 360,
9502e91208SEmmanuel Vadot 480,
9602e91208SEmmanuel Vadot 0,
9702e91208SEmmanuel Vadot 0,
9802e91208SEmmanuel Vadot 0,
9902e91208SEmmanuel Vadot 12000,
10002e91208SEmmanuel Vadot 24000,
10102e91208SEmmanuel Vadot 36000,
10202e91208SEmmanuel Vadot 48000,
10302e91208SEmmanuel Vadot 72000,
10402e91208SEmmanuel Vadot 0,
10502e91208SEmmanuel Vadot 0,
10602e91208SEmmanuel Vadot 1,
10702e91208SEmmanuel Vadot };
10802e91208SEmmanuel Vadot
10902e91208SEmmanuel Vadot #define AW_PWM_READ(sc, reg) bus_read_4((sc)->res, (reg))
11002e91208SEmmanuel Vadot #define AW_PWM_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
11102e91208SEmmanuel Vadot
11202e91208SEmmanuel Vadot static int aw_pwm_probe(device_t dev);
11302e91208SEmmanuel Vadot static int aw_pwm_attach(device_t dev);
11402e91208SEmmanuel Vadot static int aw_pwm_detach(device_t dev);
11502e91208SEmmanuel Vadot
11602e91208SEmmanuel Vadot static int
aw_pwm_probe(device_t dev)11702e91208SEmmanuel Vadot aw_pwm_probe(device_t dev)
11802e91208SEmmanuel Vadot {
11902e91208SEmmanuel Vadot if (!ofw_bus_status_okay(dev))
12002e91208SEmmanuel Vadot return (ENXIO);
12102e91208SEmmanuel Vadot
12202e91208SEmmanuel Vadot if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
12302e91208SEmmanuel Vadot return (ENXIO);
12402e91208SEmmanuel Vadot
12502e91208SEmmanuel Vadot device_set_desc(dev, "Allwinner PWM");
12602e91208SEmmanuel Vadot return (BUS_PROBE_DEFAULT);
12702e91208SEmmanuel Vadot }
12802e91208SEmmanuel Vadot
12902e91208SEmmanuel Vadot static int
aw_pwm_attach(device_t dev)13002e91208SEmmanuel Vadot aw_pwm_attach(device_t dev)
13102e91208SEmmanuel Vadot {
13202e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
13302e91208SEmmanuel Vadot uint64_t clk_freq;
13402e91208SEmmanuel Vadot uint32_t reg;
13502e91208SEmmanuel Vadot phandle_t node;
13602e91208SEmmanuel Vadot int error;
13702e91208SEmmanuel Vadot
13802e91208SEmmanuel Vadot sc = device_get_softc(dev);
13902e91208SEmmanuel Vadot sc->dev = dev;
14002e91208SEmmanuel Vadot
14102e91208SEmmanuel Vadot error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
14202e91208SEmmanuel Vadot if (error != 0) {
14302e91208SEmmanuel Vadot device_printf(dev, "cannot get clock\n");
14402e91208SEmmanuel Vadot goto fail;
14502e91208SEmmanuel Vadot }
14602e91208SEmmanuel Vadot error = clk_enable(sc->clk);
14702e91208SEmmanuel Vadot if (error != 0) {
14802e91208SEmmanuel Vadot device_printf(dev, "cannot enable clock\n");
14902e91208SEmmanuel Vadot goto fail;
15002e91208SEmmanuel Vadot }
15102e91208SEmmanuel Vadot
15202e91208SEmmanuel Vadot error = clk_get_freq(sc->clk, &sc->clk_freq);
15302e91208SEmmanuel Vadot if (error != 0) {
15402e91208SEmmanuel Vadot device_printf(dev, "cannot get clock frequency\n");
15502e91208SEmmanuel Vadot goto fail;
15602e91208SEmmanuel Vadot }
15702e91208SEmmanuel Vadot
15802e91208SEmmanuel Vadot if (bus_alloc_resources(dev, aw_pwm_spec, &sc->res) != 0) {
15902e91208SEmmanuel Vadot device_printf(dev, "cannot allocate resources for device\n");
16002e91208SEmmanuel Vadot error = ENXIO;
16102e91208SEmmanuel Vadot goto fail;
16202e91208SEmmanuel Vadot }
16302e91208SEmmanuel Vadot
16402e91208SEmmanuel Vadot /* Read the configuration left by U-Boot */
16502e91208SEmmanuel Vadot reg = AW_PWM_READ(sc, AW_PWM_CTRL);
16602e91208SEmmanuel Vadot if (reg & (AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN))
16702e91208SEmmanuel Vadot sc->enabled = true;
16802e91208SEmmanuel Vadot
16902e91208SEmmanuel Vadot reg = AW_PWM_READ(sc, AW_PWM_CTRL);
17002e91208SEmmanuel Vadot reg &= AW_PWM_CTRL_PRESCALE_MASK;
17102e91208SEmmanuel Vadot if (reg > nitems(aw_pwm_clk_prescaler)) {
17202e91208SEmmanuel Vadot device_printf(dev, "Bad prescaler %x, cannot guess current settings\n", reg);
17302e91208SEmmanuel Vadot goto skipcfg;
17402e91208SEmmanuel Vadot }
17502e91208SEmmanuel Vadot clk_freq = sc->clk_freq / aw_pwm_clk_prescaler[reg];
17602e91208SEmmanuel Vadot
17702e91208SEmmanuel Vadot reg = AW_PWM_READ(sc, AW_PWM_PERIOD);
17802e91208SEmmanuel Vadot sc->period = NS_PER_SEC /
17902e91208SEmmanuel Vadot (clk_freq / ((reg >> AW_PWM_PERIOD_TOTAL_SHIFT) & AW_PWM_PERIOD_TOTAL_MASK));
18002e91208SEmmanuel Vadot sc->duty = NS_PER_SEC /
18102e91208SEmmanuel Vadot (clk_freq / ((reg >> AW_PWM_PERIOD_ACTIVE_SHIFT) & AW_PWM_PERIOD_ACTIVE_MASK));
18202e91208SEmmanuel Vadot
18302e91208SEmmanuel Vadot skipcfg:
18402e91208SEmmanuel Vadot /*
18502e91208SEmmanuel Vadot * Note that we don't check for failure to attach pwmbus -- even without
18602e91208SEmmanuel Vadot * it we can still service clients who connect via fdt xref data.
18702e91208SEmmanuel Vadot */
18802e91208SEmmanuel Vadot node = ofw_bus_get_node(dev);
18902e91208SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev);
19002e91208SEmmanuel Vadot
1915b56413dSWarner Losh sc->busdev = device_add_child(dev, "pwmbus", DEVICE_UNIT_ANY);
19202e91208SEmmanuel Vadot
193*18250ec6SJohn Baldwin bus_attach_children(dev);
194*18250ec6SJohn Baldwin return (0);
19502e91208SEmmanuel Vadot
19602e91208SEmmanuel Vadot fail:
19702e91208SEmmanuel Vadot aw_pwm_detach(dev);
19802e91208SEmmanuel Vadot return (error);
19902e91208SEmmanuel Vadot }
20002e91208SEmmanuel Vadot
20102e91208SEmmanuel Vadot static int
aw_pwm_detach(device_t dev)20202e91208SEmmanuel Vadot aw_pwm_detach(device_t dev)
20302e91208SEmmanuel Vadot {
20402e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
20502e91208SEmmanuel Vadot int error;
20602e91208SEmmanuel Vadot
20702e91208SEmmanuel Vadot sc = device_get_softc(dev);
20802e91208SEmmanuel Vadot
20902e91208SEmmanuel Vadot if ((error = bus_generic_detach(sc->dev)) != 0) {
21002e91208SEmmanuel Vadot device_printf(sc->dev, "cannot detach child devices\n");
21102e91208SEmmanuel Vadot return (error);
21202e91208SEmmanuel Vadot }
21302e91208SEmmanuel Vadot
21402e91208SEmmanuel Vadot if (sc->res != NULL)
21502e91208SEmmanuel Vadot bus_release_resources(dev, aw_pwm_spec, &sc->res);
21602e91208SEmmanuel Vadot
21702e91208SEmmanuel Vadot return (0);
21802e91208SEmmanuel Vadot }
21902e91208SEmmanuel Vadot
22002e91208SEmmanuel Vadot static phandle_t
aw_pwm_get_node(device_t bus,device_t dev)22102e91208SEmmanuel Vadot aw_pwm_get_node(device_t bus, device_t dev)
22202e91208SEmmanuel Vadot {
22302e91208SEmmanuel Vadot
22402e91208SEmmanuel Vadot /*
22502e91208SEmmanuel Vadot * Share our controller node with our pwmbus child; it instantiates
22602e91208SEmmanuel Vadot * devices by walking the children contained within our node.
22702e91208SEmmanuel Vadot */
22802e91208SEmmanuel Vadot return ofw_bus_get_node(bus);
22902e91208SEmmanuel Vadot }
23002e91208SEmmanuel Vadot
23102e91208SEmmanuel Vadot static int
aw_pwm_channel_count(device_t dev,u_int * nchannel)23202e91208SEmmanuel Vadot aw_pwm_channel_count(device_t dev, u_int *nchannel)
23302e91208SEmmanuel Vadot {
23402e91208SEmmanuel Vadot
23502e91208SEmmanuel Vadot *nchannel = 1;
23602e91208SEmmanuel Vadot
23702e91208SEmmanuel Vadot return (0);
23802e91208SEmmanuel Vadot }
23902e91208SEmmanuel Vadot
24002e91208SEmmanuel Vadot static int
aw_pwm_channel_config(device_t dev,u_int channel,u_int period,u_int duty)24102e91208SEmmanuel Vadot aw_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty)
24202e91208SEmmanuel Vadot {
24302e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
24402e91208SEmmanuel Vadot uint64_t period_freq, duty_freq;
24502e91208SEmmanuel Vadot uint64_t clk_rate, div;
24602e91208SEmmanuel Vadot uint32_t reg;
24702e91208SEmmanuel Vadot int prescaler;
24802e91208SEmmanuel Vadot int i;
24902e91208SEmmanuel Vadot
25002e91208SEmmanuel Vadot sc = device_get_softc(dev);
25102e91208SEmmanuel Vadot
25202e91208SEmmanuel Vadot period_freq = NS_PER_SEC / period;
25302e91208SEmmanuel Vadot if (period_freq > AW_PWM_MAX_FREQ)
25402e91208SEmmanuel Vadot return (EINVAL);
25502e91208SEmmanuel Vadot
25602e91208SEmmanuel Vadot /*
25702e91208SEmmanuel Vadot * FIXME. The hardware is capable of sub-Hz frequencies, that is,
25802e91208SEmmanuel Vadot * periods longer than a second. But the current code cannot deal
25902e91208SEmmanuel Vadot * with those properly.
26002e91208SEmmanuel Vadot */
26102e91208SEmmanuel Vadot if (period_freq == 0)
26202e91208SEmmanuel Vadot return (EINVAL);
26302e91208SEmmanuel Vadot
26402e91208SEmmanuel Vadot /*
26502e91208SEmmanuel Vadot * FIXME. There is a great loss of precision when the period and the
26602e91208SEmmanuel Vadot * duty are near 1 second. In some cases period_freq and duty_freq can
26702e91208SEmmanuel Vadot * be equal even if the period and the duty are significantly different.
26802e91208SEmmanuel Vadot */
26902e91208SEmmanuel Vadot duty_freq = NS_PER_SEC / duty;
27002e91208SEmmanuel Vadot if (duty_freq < period_freq) {
27102e91208SEmmanuel Vadot device_printf(sc->dev, "duty < period\n");
27202e91208SEmmanuel Vadot return (EINVAL);
27302e91208SEmmanuel Vadot }
27402e91208SEmmanuel Vadot
27502e91208SEmmanuel Vadot /* First test without prescaler */
27602e91208SEmmanuel Vadot clk_rate = AW_PWM_MAX_FREQ;
27702e91208SEmmanuel Vadot prescaler = AW_PWM_CTRL_PRESCALE_MASK;
27802e91208SEmmanuel Vadot div = AW_PWM_MAX_FREQ / period_freq;
27902e91208SEmmanuel Vadot if ((div - 1) > AW_PWM_PERIOD_TOTAL_MASK) {
28002e91208SEmmanuel Vadot /* Test all prescaler */
28102e91208SEmmanuel Vadot for (i = 0; i < nitems(aw_pwm_clk_prescaler); i++) {
28202e91208SEmmanuel Vadot if (aw_pwm_clk_prescaler[i] == 0)
28302e91208SEmmanuel Vadot continue;
28402e91208SEmmanuel Vadot div = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i] / period_freq;
28502e91208SEmmanuel Vadot if ((div - 1) < AW_PWM_PERIOD_TOTAL_MASK ) {
28602e91208SEmmanuel Vadot prescaler = i;
28702e91208SEmmanuel Vadot clk_rate = AW_PWM_MAX_FREQ / aw_pwm_clk_prescaler[i];
28802e91208SEmmanuel Vadot break;
28902e91208SEmmanuel Vadot }
29002e91208SEmmanuel Vadot }
29102e91208SEmmanuel Vadot if (prescaler == AW_PWM_CTRL_PRESCALE_MASK)
29202e91208SEmmanuel Vadot return (EINVAL);
29302e91208SEmmanuel Vadot }
29402e91208SEmmanuel Vadot
29502e91208SEmmanuel Vadot reg = AW_PWM_READ(sc, AW_PWM_CTRL);
29602e91208SEmmanuel Vadot
29702e91208SEmmanuel Vadot /* Write the prescalar */
29802e91208SEmmanuel Vadot reg &= ~AW_PWM_CTRL_PRESCALE_MASK;
29902e91208SEmmanuel Vadot reg |= prescaler;
30002e91208SEmmanuel Vadot
30102e91208SEmmanuel Vadot reg &= ~AW_PWM_CTRL_MODE_MASK;
30202e91208SEmmanuel Vadot reg |= AW_PWM_CTRL_CYCLE_MODE;
30302e91208SEmmanuel Vadot
30402e91208SEmmanuel Vadot reg &= ~AW_PWM_CTRL_PULSE_START;
30502e91208SEmmanuel Vadot reg &= ~AW_PWM_CTRL_CLK_BYPASS;
30602e91208SEmmanuel Vadot
30702e91208SEmmanuel Vadot AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
30802e91208SEmmanuel Vadot
30902e91208SEmmanuel Vadot /* Write the total/active cycles */
31002e91208SEmmanuel Vadot reg = ((clk_rate / period_freq - 1) << AW_PWM_PERIOD_TOTAL_SHIFT) |
31102e91208SEmmanuel Vadot ((clk_rate / duty_freq) << AW_PWM_PERIOD_ACTIVE_SHIFT);
31202e91208SEmmanuel Vadot AW_PWM_WRITE(sc, AW_PWM_PERIOD, reg);
31302e91208SEmmanuel Vadot
31402e91208SEmmanuel Vadot sc->period = period;
31502e91208SEmmanuel Vadot sc->duty = duty;
31602e91208SEmmanuel Vadot
31702e91208SEmmanuel Vadot return (0);
31802e91208SEmmanuel Vadot }
31902e91208SEmmanuel Vadot
32002e91208SEmmanuel Vadot static int
aw_pwm_channel_get_config(device_t dev,u_int channel,u_int * period,u_int * duty)32102e91208SEmmanuel Vadot aw_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty)
32202e91208SEmmanuel Vadot {
32302e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
32402e91208SEmmanuel Vadot
32502e91208SEmmanuel Vadot sc = device_get_softc(dev);
32602e91208SEmmanuel Vadot
32702e91208SEmmanuel Vadot *period = sc->period;
32802e91208SEmmanuel Vadot *duty = sc->duty;
32902e91208SEmmanuel Vadot
33002e91208SEmmanuel Vadot return (0);
33102e91208SEmmanuel Vadot }
33202e91208SEmmanuel Vadot
33302e91208SEmmanuel Vadot static int
aw_pwm_channel_enable(device_t dev,u_int channel,bool enable)33402e91208SEmmanuel Vadot aw_pwm_channel_enable(device_t dev, u_int channel, bool enable)
33502e91208SEmmanuel Vadot {
33602e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
33702e91208SEmmanuel Vadot uint32_t reg;
33802e91208SEmmanuel Vadot
33902e91208SEmmanuel Vadot sc = device_get_softc(dev);
34002e91208SEmmanuel Vadot
34102e91208SEmmanuel Vadot if (enable && sc->enabled)
34202e91208SEmmanuel Vadot return (0);
34302e91208SEmmanuel Vadot
34402e91208SEmmanuel Vadot reg = AW_PWM_READ(sc, AW_PWM_CTRL);
34502e91208SEmmanuel Vadot if (enable)
34602e91208SEmmanuel Vadot reg |= AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN;
34702e91208SEmmanuel Vadot else
34802e91208SEmmanuel Vadot reg &= ~(AW_PWM_CTRL_GATE | AW_PWM_CTRL_EN);
34902e91208SEmmanuel Vadot
35002e91208SEmmanuel Vadot AW_PWM_WRITE(sc, AW_PWM_CTRL, reg);
35102e91208SEmmanuel Vadot
35202e91208SEmmanuel Vadot sc->enabled = enable;
35302e91208SEmmanuel Vadot
35402e91208SEmmanuel Vadot return (0);
35502e91208SEmmanuel Vadot }
35602e91208SEmmanuel Vadot
35702e91208SEmmanuel Vadot static int
aw_pwm_channel_is_enabled(device_t dev,u_int channel,bool * enabled)35802e91208SEmmanuel Vadot aw_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled)
35902e91208SEmmanuel Vadot {
36002e91208SEmmanuel Vadot struct aw_pwm_softc *sc;
36102e91208SEmmanuel Vadot
36202e91208SEmmanuel Vadot sc = device_get_softc(dev);
36302e91208SEmmanuel Vadot
36402e91208SEmmanuel Vadot *enabled = sc->enabled;
36502e91208SEmmanuel Vadot
36602e91208SEmmanuel Vadot return (0);
36702e91208SEmmanuel Vadot }
36802e91208SEmmanuel Vadot
36902e91208SEmmanuel Vadot static device_method_t aw_pwm_methods[] = {
37002e91208SEmmanuel Vadot /* Device interface */
37102e91208SEmmanuel Vadot DEVMETHOD(device_probe, aw_pwm_probe),
37202e91208SEmmanuel Vadot DEVMETHOD(device_attach, aw_pwm_attach),
37302e91208SEmmanuel Vadot DEVMETHOD(device_detach, aw_pwm_detach),
37402e91208SEmmanuel Vadot
37502e91208SEmmanuel Vadot /* ofw_bus interface */
37602e91208SEmmanuel Vadot DEVMETHOD(ofw_bus_get_node, aw_pwm_get_node),
37702e91208SEmmanuel Vadot
37802e91208SEmmanuel Vadot /* pwmbus interface */
37902e91208SEmmanuel Vadot DEVMETHOD(pwmbus_channel_count, aw_pwm_channel_count),
38002e91208SEmmanuel Vadot DEVMETHOD(pwmbus_channel_config, aw_pwm_channel_config),
38102e91208SEmmanuel Vadot DEVMETHOD(pwmbus_channel_get_config, aw_pwm_channel_get_config),
38202e91208SEmmanuel Vadot DEVMETHOD(pwmbus_channel_enable, aw_pwm_channel_enable),
38302e91208SEmmanuel Vadot DEVMETHOD(pwmbus_channel_is_enabled, aw_pwm_channel_is_enabled),
38402e91208SEmmanuel Vadot
38502e91208SEmmanuel Vadot DEVMETHOD_END
38602e91208SEmmanuel Vadot };
38702e91208SEmmanuel Vadot
38802e91208SEmmanuel Vadot static driver_t aw_pwm_driver = {
38902e91208SEmmanuel Vadot "pwm",
39002e91208SEmmanuel Vadot aw_pwm_methods,
39102e91208SEmmanuel Vadot sizeof(struct aw_pwm_softc),
39202e91208SEmmanuel Vadot };
39302e91208SEmmanuel Vadot
39402e91208SEmmanuel Vadot DRIVER_MODULE(aw_pwm, simplebus, aw_pwm_driver, 0, 0);
39502e91208SEmmanuel Vadot MODULE_VERSION(aw_pwm, 1);
39602e91208SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data);
397