1 /* $OpenBSD: rkpwm.c,v 1.2 2020/09/08 01:29:04 jmatthew Exp $ */ 2 /* 3 * Copyright (c) 2019 Krystian Lewandowski 4 * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <machine/fdt.h> 25 #include <machine/bus.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_clock.h> 29 #include <dev/ofw/ofw_misc.h> 30 #include <dev/ofw/ofw_pinctrl.h> 31 #include <dev/ofw/fdt.h> 32 33 #define PWM_V2_CNTR 0x00 34 #define PWM_V2_PERIOD 0x04 35 #define PWM_V2_DUTY 0x08 36 #define PWM_V2_CTRL 0x0c 37 #define PWM_V2_CTRL_ENABLE (1 << 0) 38 #define PWM_V2_CTRL_CONTINUOUS (1 << 1) 39 #define PWM_V2_CTRL_DUTY_POSITIVE (1 << 3) 40 #define PWM_V2_CTRL_INACTIVE_POSITIVE (1 << 4) 41 42 #define NS_PER_S 1000000000 43 44 #define HREAD4(sc, reg) \ 45 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 46 #define HWRITE4(sc, reg, val) \ 47 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 48 #define HSET4(sc, reg, bits) \ 49 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 50 #define HCLR4(sc, reg, bits) \ 51 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 52 53 struct rkpwm_softc { 54 struct device sc_dev; 55 bus_space_tag_t sc_iot; 56 bus_space_handle_t sc_ioh; 57 58 uint32_t sc_clkin; 59 struct pwm_device sc_pd; 60 }; 61 62 int rkpwm_match(struct device *, void *, void *); 63 void rkpwm_attach(struct device *, struct device *, void *); 64 65 struct cfattach rkpwm_ca = { 66 sizeof(struct rkpwm_softc), rkpwm_match, rkpwm_attach 67 }; 68 69 struct cfdriver rkpwm_cd = { 70 NULL, "rkpwm", DV_DULL 71 }; 72 73 int rkpwm_get_state(void *, uint32_t *, struct pwm_state *); 74 int rkpwm_set_state(void *, uint32_t *, struct pwm_state *); 75 76 int 77 rkpwm_match(struct device *parent, void *match, void *aux) 78 { 79 struct fdt_attach_args *faa = aux; 80 81 return (OF_is_compatible(faa->fa_node, "rockchip,rk3288-pwm") || 82 OF_is_compatible(faa->fa_node, "rockchip,rk3328-pwm")); 83 } 84 85 void 86 rkpwm_attach(struct device *parent, struct device *self, void *aux) 87 { 88 struct rkpwm_softc *sc = (struct rkpwm_softc *)self; 89 struct fdt_attach_args *faa = aux; 90 91 if (faa->fa_nreg < 1) { 92 printf(": no registers\n"); 93 return; 94 } 95 96 sc->sc_clkin = clock_get_frequency(faa->fa_node, "pwm"); 97 if (sc->sc_clkin == 0) { 98 printf(": no clock\n"); 99 return; 100 } 101 102 sc->sc_iot = faa->fa_iot; 103 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 104 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 105 printf(": can't map registers\n"); 106 return; 107 } 108 109 printf("\n"); 110 111 pinctrl_byname(faa->fa_node, "default"); 112 113 clock_enable_all(faa->fa_node); 114 reset_deassert_all(faa->fa_node); 115 116 sc->sc_pd.pd_node = faa->fa_node; 117 sc->sc_pd.pd_cookie = sc; 118 sc->sc_pd.pd_get_state = rkpwm_get_state; 119 sc->sc_pd.pd_set_state = rkpwm_set_state; 120 121 pwm_register(&sc->sc_pd); 122 } 123 124 int 125 rkpwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps) 126 { 127 struct rkpwm_softc *sc = cookie; 128 uint32_t idx = cells[0]; 129 uint64_t rate, cycles, act_cycles; 130 131 if (idx != 0) 132 return EINVAL; 133 134 rate = sc->sc_clkin; 135 cycles = HREAD4(sc, PWM_V2_PERIOD); 136 act_cycles = HREAD4(sc, PWM_V2_DUTY); 137 138 memset(ps, 0, sizeof(struct pwm_state)); 139 ps->ps_period = (NS_PER_S * cycles) / rate; 140 ps->ps_pulse_width = (NS_PER_S * act_cycles) / rate; 141 if (HREAD4(sc, PWM_V2_CTRL) & PWM_V2_CTRL_ENABLE) 142 ps->ps_enabled = 1; 143 144 return 0; 145 } 146 147 int 148 rkpwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps) 149 { 150 struct rkpwm_softc *sc = cookie; 151 uint32_t idx = cells[0]; 152 uint64_t rate, cycles, act_cycles; 153 154 if (idx != 0) 155 return EINVAL; 156 157 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_ENABLE | PWM_V2_CTRL_CONTINUOUS); 158 159 if (!ps->ps_enabled) 160 return 0; 161 162 rate = sc->sc_clkin; 163 cycles = (rate * ps->ps_period) / NS_PER_S; 164 act_cycles = (rate * ps->ps_pulse_width) / NS_PER_S; 165 if (cycles < 1 || act_cycles > cycles) 166 return EINVAL; 167 168 HWRITE4(sc, PWM_V2_PERIOD, cycles); 169 HWRITE4(sc, PWM_V2_DUTY, act_cycles); 170 171 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_INACTIVE_POSITIVE); 172 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_DUTY_POSITIVE); 173 174 if (ps->ps_flags & PWM_POLARITY_INVERTED) 175 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_INACTIVE_POSITIVE); 176 else 177 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_DUTY_POSITIVE); 178 179 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_ENABLE | PWM_V2_CTRL_CONTINUOUS); 180 return 0; 181 } 182