1 /* $OpenBSD: rkpwm.c,v 1.1 2019/12/03 09:08:48 patrick 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 } 83 84 void 85 rkpwm_attach(struct device *parent, struct device *self, void *aux) 86 { 87 struct rkpwm_softc *sc = (struct rkpwm_softc *)self; 88 struct fdt_attach_args *faa = aux; 89 90 if (faa->fa_nreg < 1) { 91 printf(": no registers\n"); 92 return; 93 } 94 95 sc->sc_clkin = clock_get_frequency(faa->fa_node, "pwm"); 96 if (sc->sc_clkin == 0) { 97 printf(": no clock\n"); 98 return; 99 } 100 101 sc->sc_iot = faa->fa_iot; 102 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 103 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 104 printf(": can't map registers\n"); 105 return; 106 } 107 108 printf("\n"); 109 110 pinctrl_byname(faa->fa_node, "default"); 111 112 clock_enable_all(faa->fa_node); 113 reset_deassert_all(faa->fa_node); 114 115 sc->sc_pd.pd_node = faa->fa_node; 116 sc->sc_pd.pd_cookie = sc; 117 sc->sc_pd.pd_get_state = rkpwm_get_state; 118 sc->sc_pd.pd_set_state = rkpwm_set_state; 119 120 pwm_register(&sc->sc_pd); 121 } 122 123 int 124 rkpwm_get_state(void *cookie, uint32_t *cells, struct pwm_state *ps) 125 { 126 struct rkpwm_softc *sc = cookie; 127 uint32_t idx = cells[0]; 128 uint64_t rate, cycles, act_cycles; 129 130 if (idx != 0) 131 return EINVAL; 132 133 rate = sc->sc_clkin; 134 cycles = HREAD4(sc, PWM_V2_PERIOD); 135 act_cycles = HREAD4(sc, PWM_V2_DUTY); 136 137 memset(ps, 0, sizeof(struct pwm_state)); 138 ps->ps_period = (NS_PER_S * cycles) / rate; 139 ps->ps_pulse_width = (NS_PER_S * act_cycles) / rate; 140 if (HREAD4(sc, PWM_V2_CTRL) & PWM_V2_CTRL_ENABLE) 141 ps->ps_enabled = 1; 142 143 return 0; 144 } 145 146 int 147 rkpwm_set_state(void *cookie, uint32_t *cells, struct pwm_state *ps) 148 { 149 struct rkpwm_softc *sc = cookie; 150 uint32_t idx = cells[0]; 151 uint64_t rate, cycles, act_cycles; 152 153 if (idx != 0) 154 return EINVAL; 155 156 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_ENABLE | PWM_V2_CTRL_CONTINUOUS); 157 158 if (!ps->ps_enabled) 159 return 0; 160 161 rate = sc->sc_clkin; 162 cycles = (rate * ps->ps_period) / NS_PER_S; 163 act_cycles = (rate * ps->ps_pulse_width) / NS_PER_S; 164 if (cycles < 1 || act_cycles > cycles) 165 return EINVAL; 166 167 HWRITE4(sc, PWM_V2_PERIOD, cycles); 168 HWRITE4(sc, PWM_V2_DUTY, act_cycles); 169 170 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_INACTIVE_POSITIVE); 171 HCLR4(sc, PWM_V2_CTRL, PWM_V2_CTRL_DUTY_POSITIVE); 172 173 if (ps->ps_flags & PWM_POLARITY_INVERTED) 174 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_INACTIVE_POSITIVE); 175 else 176 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_DUTY_POSITIVE); 177 178 HSET4(sc, PWM_V2_CTRL, PWM_V2_CTRL_ENABLE | PWM_V2_CTRL_CONTINUOUS); 179 return 0; 180 } 181