1 /* $NetBSD: sunxi_ccu_phase.c,v 1.1 2017/07/17 23:26:17 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 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 ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * 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/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_phase.c,v 1.1 2017/07/17 23:26:17 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 35 #include <dev/clk/clk_backend.h> 36 37 #include <arm/sunxi/sunxi_ccu.h> 38 39 static u_int 40 sunxi_ccu_phase_get_parent_rate(struct clk *clkp) 41 { 42 struct clk *clkp_parent; 43 44 clkp_parent = clk_get_parent(clkp); 45 if (clkp_parent == NULL) 46 return 0; 47 48 return clk_get_rate(clkp_parent); 49 } 50 51 static u_int 52 sunxi_ccu_phase_div(u_int n, u_int d) 53 { 54 return (n + (d/2)) / d; 55 } 56 57 u_int 58 sunxi_ccu_phase_get_rate(struct sunxi_ccu_softc *sc, 59 struct sunxi_ccu_clk *clk) 60 { 61 struct sunxi_ccu_phase *phase = &clk->u.phase; 62 struct clk *clkp = &clk->base; 63 u_int p_rate, gp_rate, p_div, delay; 64 uint32_t val; 65 66 KASSERT(clk->type == SUNXI_CCU_PHASE); 67 68 p_rate = sunxi_ccu_phase_get_parent_rate(clkp); 69 if (p_rate == 0) 70 return 0; 71 gp_rate = sunxi_ccu_phase_get_parent_rate(clk_get_parent(clkp)); 72 if (gp_rate == 0) 73 return 0; 74 75 p_div = gp_rate / p_rate; 76 77 val = CCU_READ(sc, phase->reg); 78 delay = __SHIFTOUT(val, phase->mask); 79 80 return delay * sunxi_ccu_phase_div(360, p_div); 81 } 82 83 int 84 sunxi_ccu_phase_set_rate(struct sunxi_ccu_softc *sc, 85 struct sunxi_ccu_clk *clk, u_int new_rate) 86 { 87 struct sunxi_ccu_phase *phase = &clk->u.phase; 88 struct clk *clkp = &clk->base; 89 u_int p_rate, gp_rate, p_div, delay; 90 uint32_t val; 91 92 KASSERT(clk->type == SUNXI_CCU_PHASE); 93 94 clkp = &clk->base; 95 96 p_rate = sunxi_ccu_phase_get_parent_rate(clkp); 97 if (p_rate == 0) 98 return 0; 99 gp_rate = sunxi_ccu_phase_get_parent_rate(clk_get_parent(clkp)); 100 if (gp_rate == 0) 101 return 0; 102 103 p_div = gp_rate / p_rate; 104 105 delay = new_rate == 180 ? 0 : 106 sunxi_ccu_phase_div(new_rate, 107 sunxi_ccu_phase_div(360, p_div)); 108 109 val = CCU_READ(sc, phase->reg); 110 val &= ~phase->mask; 111 val |= __SHIFTIN(delay, phase->mask); 112 CCU_WRITE(sc, phase->reg, val); 113 114 return 0; 115 } 116 117 const char * 118 sunxi_ccu_phase_get_parent(struct sunxi_ccu_softc *sc, 119 struct sunxi_ccu_clk *clk) 120 { 121 struct sunxi_ccu_phase *phase = &clk->u.phase; 122 123 KASSERT(clk->type == SUNXI_CCU_PHASE); 124 125 return phase->parent; 126 } 127