1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 33 #include <dev/clk/clk.h> 34 35 #include <dev/clk/rockchip/rk_clk_gate.h> 36 37 #include "clkdev_if.h" 38 39 #define WR4(_clk, off, val) \ 40 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) 41 #define RD4(_clk, off, val) \ 42 CLKDEV_READ_4(clknode_get_device(_clk), off, val) 43 #define MD4(_clk, off, clr, set ) \ 44 CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) 45 #define DEVICE_LOCK(_clk) \ 46 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 47 #define DEVICE_UNLOCK(_clk) \ 48 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 49 50 static int rk_clk_gate_init(struct clknode *clk, device_t dev); 51 static int rk_clk_gate_set_gate(struct clknode *clk, bool enable); 52 struct rk_clk_gate_sc { 53 uint32_t offset; 54 uint32_t shift; 55 uint32_t mask; 56 uint32_t on_value; 57 uint32_t off_value; 58 int gate_flags; 59 bool ungated; 60 }; 61 62 static clknode_method_t rk_clk_gate_methods[] = { 63 /* Device interface */ 64 CLKNODEMETHOD(clknode_init, rk_clk_gate_init), 65 CLKNODEMETHOD(clknode_set_gate, rk_clk_gate_set_gate), 66 CLKNODEMETHOD_END 67 }; 68 DEFINE_CLASS_1(rk_clk_gate, rk_clk_gate_class, rk_clk_gate_methods, 69 sizeof(struct rk_clk_gate_sc), clknode_class); 70 71 static int 72 rk_clk_gate_init(struct clknode *clk, device_t dev) 73 { 74 uint32_t reg; 75 struct rk_clk_gate_sc *sc; 76 int rv; 77 78 sc = clknode_get_softc(clk); 79 DEVICE_LOCK(clk); 80 rv = RD4(clk, sc->offset, ®); 81 DEVICE_UNLOCK(clk); 82 if (rv != 0) 83 return (rv); 84 reg = (reg >> sc->shift) & sc->mask; 85 sc->ungated = reg == sc->on_value ? 1 : 0; 86 clknode_init_parent_idx(clk, 0); 87 return(0); 88 } 89 90 static int 91 rk_clk_gate_set_gate(struct clknode *clk, bool enable) 92 { 93 uint32_t reg; 94 struct rk_clk_gate_sc *sc; 95 int rv; 96 97 sc = clknode_get_softc(clk); 98 sc->ungated = enable; 99 DEVICE_LOCK(clk); 100 rv = MD4(clk, sc->offset, sc->mask << sc->shift, 101 ((sc->ungated ? sc->on_value : sc->off_value) << sc->shift) | 102 RK_CLK_GATE_MASK); 103 if (rv != 0) { 104 DEVICE_UNLOCK(clk); 105 return (rv); 106 } 107 RD4(clk, sc->offset, ®); 108 DEVICE_UNLOCK(clk); 109 return(0); 110 } 111 112 int 113 rk_clk_gate_register(struct clkdom *clkdom, struct rk_clk_gate_def *clkdef) 114 { 115 struct clknode *clk; 116 struct rk_clk_gate_sc *sc; 117 118 clk = clknode_create(clkdom, &rk_clk_gate_class, &clkdef->clkdef); 119 if (clk == NULL) 120 return (1); 121 122 sc = clknode_get_softc(clk); 123 sc->offset = clkdef->offset; 124 sc->shift = clkdef->shift; 125 sc->mask = clkdef->mask; 126 sc->on_value = clkdef->on_value; 127 sc->off_value = clkdef->off_value; 128 sc->gate_flags = clkdef->gate_flags; 129 130 clknode_register(clkdom, clk); 131 return (0); 132 } 133