1*90313c06Smsaitoh /* $NetBSD: meson_pwm.c,v 1.6 2024/02/07 04:20:26 msaitoh Exp $ */
28afae5d5Sryo
38afae5d5Sryo /*
4*90313c06Smsaitoh * Copyright (c) 2021 Ryo Shimizu
58afae5d5Sryo * All rights reserved.
68afae5d5Sryo *
78afae5d5Sryo * Redistribution and use in source and binary forms, with or without
88afae5d5Sryo * modification, are permitted provided that the following conditions
98afae5d5Sryo * are met:
108afae5d5Sryo * 1. Redistributions of source code must retain the above copyright
118afae5d5Sryo * notice, this list of conditions and the following disclaimer.
128afae5d5Sryo * 2. Redistributions in binary form must reproduce the above copyright
138afae5d5Sryo * notice, this list of conditions and the following disclaimer in the
148afae5d5Sryo * documentation and/or other materials provided with the distribution.
158afae5d5Sryo *
168afae5d5Sryo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
178afae5d5Sryo * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
188afae5d5Sryo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198afae5d5Sryo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
208afae5d5Sryo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
218afae5d5Sryo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
228afae5d5Sryo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238afae5d5Sryo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
248afae5d5Sryo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
258afae5d5Sryo * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
268afae5d5Sryo * POSSIBILITY OF SUCH DAMAGE.
278afae5d5Sryo */
288afae5d5Sryo
298afae5d5Sryo #include <sys/cdefs.h>
30*90313c06Smsaitoh __KERNEL_RCSID(0, "$NetBSD: meson_pwm.c,v 1.6 2024/02/07 04:20:26 msaitoh Exp $");
318afae5d5Sryo
328afae5d5Sryo #include <sys/param.h>
338afae5d5Sryo #include <sys/types.h>
348afae5d5Sryo #include <sys/bus.h>
358afae5d5Sryo #include <sys/device.h>
368afae5d5Sryo
378afae5d5Sryo #include <dev/fdt/fdtvar.h>
388afae5d5Sryo #include <dev/pwm/pwmvar.h>
398afae5d5Sryo
408afae5d5Sryo /*#define MESON_PWM_DEBUG*/
418afae5d5Sryo
428afae5d5Sryo #define MESON_PWM_DUTYCYCLE_A_REG 0x00
438afae5d5Sryo #define MESON_PWM_DUTYCYCLE_B_REG 0x01
448afae5d5Sryo #define MESON_PWM_DUTYCYCLE_HIGH __BITS(31,16)
458afae5d5Sryo #define MESON_PWM_DUTYCYCLE_LOW __BITS(15,0)
468afae5d5Sryo #define MESON_PWM_MISC_AB_REG 0x02
478afae5d5Sryo #define MESON_PWM_MISC_AB_B_CLK_EN __BIT(23)
488afae5d5Sryo #define MESON_PWM_MISC_AB_B_CLK_DIV __BITS(22,16)
498afae5d5Sryo #define MESON_PWM_MISC_AB_A_CLK_EN __BIT(15)
508afae5d5Sryo #define MESON_PWM_MISC_AB_A_CLK_DIV __BITS(14,8)
518afae5d5Sryo #define MESON_PWM_MISC_AB_B_CLK_SEL __BITS(7,6)
528afae5d5Sryo #define MESON_PWM_MISC_AB_A_CLK_SEL __BITS(5,4)
538afae5d5Sryo #define MESON_PWM_MISC_AB_DS_B_EN __BIT(3)
548afae5d5Sryo #define MESON_PWM_MISC_AB_DS_A_EN __BIT(2)
558afae5d5Sryo #define MESON_PWM_MISC_AB_PWM_B_EN __BIT(1)
568afae5d5Sryo #define MESON_PWM_MISC_AB_PWM_A_EN __BIT(0)
578afae5d5Sryo #define MESON_PWM_DELTASIGMA_A_B_REG 0x03
588afae5d5Sryo #define MESON_PWM_TIME_AB_REG 0x04
598afae5d5Sryo #define MESON_PWM_A2_REG 0x05
608afae5d5Sryo #define MESON_PWM_B2_REG 0x06
618afae5d5Sryo #define MESON_PWM_BLINK_AB_REG 0x07
628afae5d5Sryo
638afae5d5Sryo #define PWM_READ_REG(sc, reg) \
648afae5d5Sryo bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg) * 4)
658afae5d5Sryo #define PWM_WRITE_REG(sc, reg, val) \
668afae5d5Sryo bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg) * 4, (val))
678afae5d5Sryo
68646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
69646c0f59Sthorpej { .compat = "amlogic,meson-g12a-ao-pwm-ab" },
70646c0f59Sthorpej { .compat = "amlogic,meson-g12a-ao-pwm-cd" },
71646c0f59Sthorpej { .compat = "amlogic,meson-g12a-ee-pwm" },
722dcdd1cdSthorpej DEVICE_COMPAT_EOL
738afae5d5Sryo };
748afae5d5Sryo
758afae5d5Sryo #define MESON_PWM_NCHAN 2
768afae5d5Sryo struct meson_pwm_channel {
778afae5d5Sryo struct meson_pwm_softc *mpc_sc;
788afae5d5Sryo struct pwm_controller mpc_pwm;
798afae5d5Sryo struct pwm_config mpc_conf;
808afae5d5Sryo u_int mpc_index;
818afae5d5Sryo };
828afae5d5Sryo
838afae5d5Sryo struct meson_pwm_softc {
848afae5d5Sryo device_t sc_dev;
858afae5d5Sryo bus_space_tag_t sc_bst;
868afae5d5Sryo bus_space_handle_t sc_bsh;
878afae5d5Sryo kmutex_t sc_reglock; /* for PWM_A vs. PWM_B */
888afae5d5Sryo int sc_phandle;
898afae5d5Sryo u_int sc_clkfreq;
908afae5d5Sryo u_int sc_clksource;
918afae5d5Sryo struct meson_pwm_channel sc_pwmchan[MESON_PWM_NCHAN];
928afae5d5Sryo };
938afae5d5Sryo
948afae5d5Sryo static int
meson_pwm_enable(pwm_tag_t pwm,bool enable)958afae5d5Sryo meson_pwm_enable(pwm_tag_t pwm, bool enable)
968afae5d5Sryo {
978afae5d5Sryo struct meson_pwm_channel * const pwmchan = pwm->pwm_priv;
988afae5d5Sryo struct meson_pwm_softc * const sc = device_private(pwm->pwm_dev);
998afae5d5Sryo uint32_t val;
1008afae5d5Sryo
1018afae5d5Sryo mutex_enter(&sc->sc_reglock);
1028afae5d5Sryo switch (pwmchan->mpc_index) {
1038afae5d5Sryo case 0: /* A */
1048afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
1058afae5d5Sryo val &= ~MESON_PWM_MISC_AB_DS_A_EN;
1068afae5d5Sryo val |= MESON_PWM_MISC_AB_PWM_A_EN;
1078afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_MISC_AB_REG, val);
1088afae5d5Sryo break;
1098afae5d5Sryo case 1: /* B */
1108afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
1118afae5d5Sryo val &= ~MESON_PWM_MISC_AB_DS_B_EN;
1128afae5d5Sryo val |= MESON_PWM_MISC_AB_PWM_B_EN;
1138afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_MISC_AB_REG, val);
1148afae5d5Sryo break;
1158afae5d5Sryo }
1168afae5d5Sryo mutex_exit(&sc->sc_reglock);
1178afae5d5Sryo
1188afae5d5Sryo return 0;
1198afae5d5Sryo }
1208afae5d5Sryo
1218afae5d5Sryo static void
meson_pwm_get_current(struct meson_pwm_softc * sc,int chan,struct pwm_config * conf)1228afae5d5Sryo meson_pwm_get_current(struct meson_pwm_softc *sc, int chan,
1238afae5d5Sryo struct pwm_config *conf)
1248afae5d5Sryo {
1258afae5d5Sryo uint64_t period_hz, duty_hz;
1268afae5d5Sryo uint32_t val;
1278afae5d5Sryo u_int period, duty, clk_div, hi, lo;
1288afae5d5Sryo
1298afae5d5Sryo memset(conf, 0, sizeof(*conf));
1308afae5d5Sryo
1318afae5d5Sryo mutex_enter(&sc->sc_reglock);
1328afae5d5Sryo switch (chan) {
1338afae5d5Sryo case 0: /* A */
1348afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
1358afae5d5Sryo clk_div = __SHIFTOUT(val, MESON_PWM_MISC_AB_A_CLK_DIV);
1368afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_DUTYCYCLE_A_REG);
1378afae5d5Sryo hi = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_HIGH);
1388afae5d5Sryo lo = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_LOW);
1398afae5d5Sryo break;
1408afae5d5Sryo case 1: /* B */
1418afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
1428afae5d5Sryo clk_div = __SHIFTOUT(val, MESON_PWM_MISC_AB_B_CLK_DIV);
1438afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_DUTYCYCLE_B_REG);
1448afae5d5Sryo hi = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_HIGH);
1458afae5d5Sryo lo = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_LOW);
1468afae5d5Sryo break;
1478afae5d5Sryo default:
1488afae5d5Sryo mutex_exit(&sc->sc_reglock);
1498afae5d5Sryo return;
1508afae5d5Sryo }
1518afae5d5Sryo mutex_exit(&sc->sc_reglock);
1528afae5d5Sryo
1538afae5d5Sryo clk_div += 1;
1548afae5d5Sryo duty_hz = (uint64_t)hi * clk_div;
1558afae5d5Sryo period_hz = (uint64_t)(hi + lo) * clk_div;
1568afae5d5Sryo
1578afae5d5Sryo period = period_hz * 1000000000ULL / sc->sc_clkfreq;
1588afae5d5Sryo duty = duty_hz * 1000000000ULL / sc->sc_clkfreq;
1598afae5d5Sryo
1608afae5d5Sryo conf->polarity = PWM_ACTIVE_HIGH;
1618afae5d5Sryo conf->period = period;
1628afae5d5Sryo conf->duty_cycle = duty;
1638afae5d5Sryo }
1648afae5d5Sryo
1658afae5d5Sryo static int
meson_pwm_get_config(pwm_tag_t pwm,struct pwm_config * conf)1668afae5d5Sryo meson_pwm_get_config(pwm_tag_t pwm, struct pwm_config *conf)
1678afae5d5Sryo {
1688afae5d5Sryo struct meson_pwm_channel * const pwmchan = pwm->pwm_priv;
1698afae5d5Sryo
1708afae5d5Sryo *conf = pwmchan->mpc_conf;
1718afae5d5Sryo return 0;
1728afae5d5Sryo }
1738afae5d5Sryo
1748afae5d5Sryo static int
meson_pwm_set_config(pwm_tag_t pwm,const struct pwm_config * conf)1758afae5d5Sryo meson_pwm_set_config(pwm_tag_t pwm, const struct pwm_config *conf)
1768afae5d5Sryo {
1778afae5d5Sryo struct meson_pwm_channel * const pwmchan = pwm->pwm_priv;
1788afae5d5Sryo struct meson_pwm_softc * const sc = device_private(pwm->pwm_dev);
1798afae5d5Sryo uint64_t period_hz, duty_hz;
1808afae5d5Sryo uint32_t val;
1818afae5d5Sryo u_int period, duty, clk_div, hi, lo;
1828afae5d5Sryo #ifdef MESON_PWM_DEBUG
1838afae5d5Sryo u_int old_div = 0, old_hi = 0, old_lo = 0;
1848afae5d5Sryo #endif
1858afae5d5Sryo
1868afae5d5Sryo period = conf->period;
1878afae5d5Sryo duty = conf->duty_cycle;
1888afae5d5Sryo if (period == 0)
1898afae5d5Sryo return EINVAL;
1908afae5d5Sryo KASSERT(period >= duty);
1918afae5d5Sryo if (conf->polarity == PWM_ACTIVE_LOW)
1928afae5d5Sryo duty = period - duty;
1938afae5d5Sryo
1948afae5d5Sryo /* calculate the period to be within the maximum value (0xffff) */
1958afae5d5Sryo #define MESON_PWMTIME_MAX 0xffff
1968afae5d5Sryo #define MESON_CLKDIV_MAX 127
1978afae5d5Sryo clk_div = 1;
1988afae5d5Sryo period_hz = ((uint64_t)sc->sc_clkfreq * period + 500000000ULL) /
1998afae5d5Sryo 1000000000ULL;
2008afae5d5Sryo duty_hz = ((uint64_t)sc->sc_clkfreq * duty + 500000000ULL) /
2018afae5d5Sryo 1000000000ULL;
2028afae5d5Sryo if (period_hz > MESON_PWMTIME_MAX) {
2038afae5d5Sryo clk_div = (period_hz + 0x7fff) / 0xffff;
2048afae5d5Sryo period_hz /= clk_div;
2058afae5d5Sryo duty_hz /= clk_div;
2068afae5d5Sryo }
2078afae5d5Sryo
2088afae5d5Sryo clk_div -= 1; /* the divider is N+1 */
2098afae5d5Sryo if (clk_div > MESON_CLKDIV_MAX)
2108afae5d5Sryo return EINVAL;
2118afae5d5Sryo
2128afae5d5Sryo hi = duty_hz;
2138afae5d5Sryo lo = period_hz - duty_hz;
2148afae5d5Sryo
2158afae5d5Sryo mutex_enter(&sc->sc_reglock);
2168afae5d5Sryo switch (pwmchan->mpc_index) {
2178afae5d5Sryo case 0: /* A */
2188afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
2198afae5d5Sryo val &= ~MESON_PWM_MISC_AB_A_CLK_DIV;
2208afae5d5Sryo #ifdef MESON_PWM_DEBUG
2218afae5d5Sryo old_div = __SHIFTOUT(val, MESON_PWM_MISC_AB_A_CLK_DIV);
2228afae5d5Sryo #endif
2238afae5d5Sryo val |= __SHIFTIN(clk_div, MESON_PWM_MISC_AB_A_CLK_DIV);
2248afae5d5Sryo val &= ~MESON_PWM_MISC_AB_A_CLK_SEL;
2258afae5d5Sryo val |= __SHIFTIN(sc->sc_clksource, MESON_PWM_MISC_AB_A_CLK_SEL);
2268afae5d5Sryo val |= MESON_PWM_MISC_AB_A_CLK_EN;
2278afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_MISC_AB_REG, val);
2288afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_DUTYCYCLE_A_REG);
2298afae5d5Sryo #ifdef MESON_PWM_DEBUG
2308afae5d5Sryo old_hi = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_HIGH);
2318afae5d5Sryo old_lo = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_LOW);
2328afae5d5Sryo #endif
2338afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_DUTYCYCLE_A_REG,
2348afae5d5Sryo __SHIFTIN(hi, MESON_PWM_DUTYCYCLE_HIGH) |
2358afae5d5Sryo __SHIFTIN(lo, MESON_PWM_DUTYCYCLE_LOW));
2368afae5d5Sryo break;
2378afae5d5Sryo case 1: /* B */
2388afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_MISC_AB_REG);
2398afae5d5Sryo val &= ~MESON_PWM_MISC_AB_B_CLK_DIV;
2408afae5d5Sryo #ifdef MESON_PWM_DEBUG
2418afae5d5Sryo old_div = __SHIFTOUT(val, MESON_PWM_MISC_AB_B_CLK_DIV);
2428afae5d5Sryo #endif
2438afae5d5Sryo val |= __SHIFTIN(clk_div, MESON_PWM_MISC_AB_B_CLK_DIV);
2448afae5d5Sryo val &= ~MESON_PWM_MISC_AB_B_CLK_SEL;
2458afae5d5Sryo val |= __SHIFTIN(sc->sc_clksource, MESON_PWM_MISC_AB_B_CLK_SEL);
2468afae5d5Sryo val |= MESON_PWM_MISC_AB_B_CLK_EN;
2478afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_MISC_AB_REG, val);
2488afae5d5Sryo val = PWM_READ_REG(sc, MESON_PWM_DUTYCYCLE_B_REG);
2498afae5d5Sryo #ifdef MESON_PWM_DEBUG
2508afae5d5Sryo old_hi = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_HIGH);
2518afae5d5Sryo old_lo = __SHIFTOUT(val, MESON_PWM_DUTYCYCLE_LOW);
2528afae5d5Sryo #endif
2538afae5d5Sryo PWM_WRITE_REG(sc, MESON_PWM_DUTYCYCLE_B_REG,
2548afae5d5Sryo __SHIFTIN(hi, MESON_PWM_DUTYCYCLE_HIGH) |
2558afae5d5Sryo __SHIFTIN(lo, MESON_PWM_DUTYCYCLE_LOW));
2568afae5d5Sryo break;
2578afae5d5Sryo }
2588afae5d5Sryo mutex_exit(&sc->sc_reglock);
2598afae5d5Sryo
2608afae5d5Sryo pwmchan->mpc_conf = *conf;
2618afae5d5Sryo
2628afae5d5Sryo #ifdef MESON_PWM_DEBUG
2638afae5d5Sryo device_printf(sc->sc_dev,
2648afae5d5Sryo "%s: %s: polarity=%s, DutuCycle/Period=%uns/%uns(%u%%) : "
2658afae5d5Sryo "%uHz, HIGH:LOW=%u:%u(%u%%) -> "
2668afae5d5Sryo "%uHz, HIGH:LOW=%u:%u(%u%%)\n", __func__,
2678afae5d5Sryo pwmchan->mpc_index ? "A" : "B",
2688afae5d5Sryo (conf->polarity == PWM_ACTIVE_LOW) ? "LOW" : "HIGH",
2698afae5d5Sryo conf->duty_cycle, conf->period,
2708afae5d5Sryo conf->duty_cycle * 100 / conf->period,
2718afae5d5Sryo sc->sc_clkfreq / (old_div + 1), old_hi, old_lo,
2728afae5d5Sryo old_hi * 100 / (old_hi + old_lo),
2738afae5d5Sryo sc->sc_clkfreq / (clk_div + 1), hi, lo, hi * 100 / (hi + lo));
2748afae5d5Sryo #endif
2758afae5d5Sryo return 0;
2768afae5d5Sryo }
2778afae5d5Sryo
2788afae5d5Sryo static pwm_tag_t
meson_pwm_get_tag(device_t dev,const void * data,size_t len)2798afae5d5Sryo meson_pwm_get_tag(device_t dev, const void *data, size_t len)
2808afae5d5Sryo {
2818afae5d5Sryo struct meson_pwm_softc * const sc = device_private(dev);
2828afae5d5Sryo struct meson_pwm_channel *pwmchan;
2838afae5d5Sryo const u_int *pwm = data;
2848afae5d5Sryo
2858afae5d5Sryo if (len != 16)
2868afae5d5Sryo return NULL;
2878afae5d5Sryo
2888afae5d5Sryo const u_int index = be32toh(pwm[1]);
2898afae5d5Sryo if (index >= MESON_PWM_NCHAN)
2908afae5d5Sryo return NULL;
2918afae5d5Sryo const u_int period = be32toh(pwm[2]);
2928afae5d5Sryo const u_int polarity = (pwm[3] == 0) ? PWM_ACTIVE_HIGH : PWM_ACTIVE_LOW;
2938afae5d5Sryo
2948afae5d5Sryo pwmchan = &sc->sc_pwmchan[index];
2958afae5d5Sryo
2968afae5d5Sryo /*
2978afae5d5Sryo * if polarity or period in pwm-tag is different from the copy of
2988afae5d5Sryo * config it holds, the content returned by pwm_get_conf() should
2998afae5d5Sryo * also be according to the tag.
3008afae5d5Sryo * this is because the caller may only set_conf() if necessary.
3018afae5d5Sryo */
3028afae5d5Sryo if (pwmchan->mpc_conf.polarity != polarity) {
3038afae5d5Sryo pwmchan->mpc_conf.duty_cycle =
3048afae5d5Sryo pwmchan->mpc_conf.period - pwmchan->mpc_conf.duty_cycle;
3058afae5d5Sryo pwmchan->mpc_conf.polarity = polarity;
3068afae5d5Sryo }
3078afae5d5Sryo if (pwmchan->mpc_conf.period != period) {
3088afae5d5Sryo if (pwmchan->mpc_conf.period == 0) {
3098afae5d5Sryo pwmchan->mpc_conf.duty_cycle = 0;
3108afae5d5Sryo } else {
3118afae5d5Sryo pwmchan->mpc_conf.duty_cycle =
3128afae5d5Sryo (uint64_t)pwmchan->mpc_conf.duty_cycle *
3138afae5d5Sryo period / pwmchan->mpc_conf.period;
3148afae5d5Sryo }
3158afae5d5Sryo pwmchan->mpc_conf.period = period;
3168afae5d5Sryo }
3178afae5d5Sryo
3188afae5d5Sryo return &pwmchan->mpc_pwm;
3198afae5d5Sryo }
3208afae5d5Sryo
3218afae5d5Sryo static struct fdtbus_pwm_controller_func meson_pwm_funcs = {
3228afae5d5Sryo .get_tag = meson_pwm_get_tag
3238afae5d5Sryo };
3248afae5d5Sryo
3258afae5d5Sryo static int
meson_pwm_match(device_t parent,cfdata_t cf,void * aux)3268afae5d5Sryo meson_pwm_match(device_t parent, cfdata_t cf, void *aux)
3278afae5d5Sryo {
3288afae5d5Sryo struct fdt_attach_args * const faa = aux;
3298afae5d5Sryo
3306e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
3318afae5d5Sryo }
3328afae5d5Sryo
3338afae5d5Sryo static void
meson_pwm_attach(device_t parent,device_t self,void * aux)3348afae5d5Sryo meson_pwm_attach(device_t parent, device_t self, void *aux)
3358afae5d5Sryo {
3368afae5d5Sryo struct meson_pwm_softc * const sc = device_private(self);
3378afae5d5Sryo struct fdt_attach_args * const faa = aux;
3388afae5d5Sryo bus_addr_t addr;
3398afae5d5Sryo bus_size_t size;
3408afae5d5Sryo struct clk *clk;
3418afae5d5Sryo int phandle, i;
3428afae5d5Sryo
3438afae5d5Sryo sc->sc_dev = self;
3448afae5d5Sryo sc->sc_bst = faa->faa_bst;
3458afae5d5Sryo sc->sc_phandle = phandle = faa->faa_phandle;
3468afae5d5Sryo
3478afae5d5Sryo clk = fdtbus_clock_get_index(phandle, 0);
3488afae5d5Sryo if (clk == NULL) {
3498afae5d5Sryo aprint_error(": couldn't get clock\n");
3508afae5d5Sryo return;
3518afae5d5Sryo }
3528afae5d5Sryo sc->sc_clkfreq = clk_get_rate(clk);
3538afae5d5Sryo
3548afae5d5Sryo if (clk == fdtbus_clock_byname("vid_pll"))
3558afae5d5Sryo sc->sc_clksource = 1;
3568afae5d5Sryo else if (clk == fdtbus_clock_byname("fclk_div4"))
3578afae5d5Sryo sc->sc_clksource = 2;
3588afae5d5Sryo else if (clk == fdtbus_clock_byname("fclk_div3"))
3598afae5d5Sryo sc->sc_clksource = 3;
3608afae5d5Sryo else
3618afae5d5Sryo sc->sc_clksource = 0; /* default: "xtal" */
3628afae5d5Sryo
3638afae5d5Sryo if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
3648afae5d5Sryo aprint_error(": couldn't get registers\n");
3658afae5d5Sryo return;
3668afae5d5Sryo }
3678afae5d5Sryo if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
3688afae5d5Sryo aprint_error(": couldn't map registers\n");
3698afae5d5Sryo return;
3708afae5d5Sryo }
3718afae5d5Sryo
3728afae5d5Sryo aprint_naive("\n");
3738afae5d5Sryo aprint_normal(": Pulse-Width Modulation\n");
3748afae5d5Sryo
3758afae5d5Sryo mutex_init(&sc->sc_reglock, MUTEX_DEFAULT, IPL_NONE);
3768afae5d5Sryo for (i = 0; i < __arraycount(sc->sc_pwmchan); i++) {
3778afae5d5Sryo sc->sc_pwmchan[i].mpc_sc = sc;
3788afae5d5Sryo sc->sc_pwmchan[i].mpc_index = i;
3798afae5d5Sryo sc->sc_pwmchan[i].mpc_pwm.pwm_enable = meson_pwm_enable;
3808afae5d5Sryo sc->sc_pwmchan[i].mpc_pwm.pwm_get_config = meson_pwm_get_config;
3818afae5d5Sryo sc->sc_pwmchan[i].mpc_pwm.pwm_set_config = meson_pwm_set_config;
3828afae5d5Sryo sc->sc_pwmchan[i].mpc_pwm.pwm_dev = self;
3838afae5d5Sryo sc->sc_pwmchan[i].mpc_pwm.pwm_priv = &sc->sc_pwmchan[i];
3848afae5d5Sryo meson_pwm_get_current(sc, i, &sc->sc_pwmchan[i].mpc_conf);
3858afae5d5Sryo }
3868afae5d5Sryo
3878afae5d5Sryo fdtbus_register_pwm_controller(self, phandle, &meson_pwm_funcs);
3888afae5d5Sryo }
3898afae5d5Sryo
3908afae5d5Sryo CFATTACH_DECL_NEW(meson_pwm, sizeof(struct meson_pwm_softc),
3918afae5d5Sryo meson_pwm_match, meson_pwm_attach, NULL, NULL);
392