Module Name: src Committed By: bouyer Date: Mon Mar 19 16:18:31 UTC 2018
Modified Files: src/sys/arch/arm/sunxi: files.sunxi sun4i_a10_ccu.c sunxi_ccu.c sunxi_ccu.h Added Files: src/sys/arch/arm/sunxi: sunxi_ccu_fractional.c Log Message: Add some more A10/A20 clocks definitions; related to display engines. The video PLLs requires a new clock type, SUNXI_CCU_FRACTIONAL To generate a diff of this commit: cvs rdiff -u -r1.44 -r1.45 src/sys/arch/arm/sunxi/files.sunxi cvs rdiff -u -r1.6 -r1.7 src/sys/arch/arm/sunxi/sun4i_a10_ccu.c cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/sunxi/sunxi_ccu.c cvs rdiff -u -r1.15 -r1.16 src/sys/arch/arm/sunxi/sunxi_ccu.h cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/sunxi/files.sunxi diff -u src/sys/arch/arm/sunxi/files.sunxi:1.44 src/sys/arch/arm/sunxi/files.sunxi:1.45 --- src/sys/arch/arm/sunxi/files.sunxi:1.44 Sat Mar 17 18:34:09 2018 +++ src/sys/arch/arm/sunxi/files.sunxi Mon Mar 19 16:18:30 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.sunxi,v 1.44 2018/03/17 18:34:09 ryo Exp $ +# $NetBSD: files.sunxi,v 1.45 2018/03/19 16:18:30 bouyer Exp $ # # Configuration info for Allwinner sunxi family SoCs # @@ -22,6 +22,7 @@ define sunxi_ccu file arch/arm/sunxi/sunxi_ccu.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_div.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_fixed_factor.c sunxi_ccu +file arch/arm/sunxi/sunxi_ccu_fractional.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_gate.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_nm.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_nkmp.c sunxi_ccu Index: src/sys/arch/arm/sunxi/sun4i_a10_ccu.c diff -u src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.6 src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.7 --- src/sys/arch/arm/sunxi/sun4i_a10_ccu.c:1.6 Sat Dec 16 16:40:33 2017 +++ src/sys/arch/arm/sunxi/sun4i_a10_ccu.c Mon Mar 19 16:18:30 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sun4i_a10_ccu.c,v 1.6 2017/12/16 16:40:33 jmcneill Exp $ */ +/* $NetBSD: sun4i_a10_ccu.c,v 1.7 2018/03/19 16:18:30 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: sun4i_a10_ccu.c,v 1.6 2017/12/16 16:40:33 jmcneill Exp $"); +__KERNEL_RCSID(1, "$NetBSD: sun4i_a10_ccu.c,v 1.7 2018/03/19 16:18:30 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -43,7 +43,10 @@ __KERNEL_RCSID(1, "$NetBSD: sun4i_a10_cc #define PLL1_CFG_REG 0x000 #define PLL2_CFG_REG 0x008 +#define PLL3_CFG_REG 0x010 +#define PLL5_CFG_REG 0x020 #define PLL6_CFG_REG 0x028 +#define PLL7_CFG_REG 0x030 #define OSC24M_CFG_REG 0x050 #define CPU_AHB_APB0_CFG_REG 0x054 #define APB1_CLK_DIV_REG 0x058 @@ -58,11 +61,20 @@ __KERNEL_RCSID(1, "$NetBSD: sun4i_a10_cc #define SD3_SCLK_CFG_REG 0x094 #define SATA_CFG_REG 0x0c8 #define USBPHY_CFG_REG 0x0cc -#define BE_CFG_REG 0x104 -#define FE_CFG_REG 0x10c +#define DRAM_GATING_REG 0x100 +#define BE0_CFG_REG 0x104 +#define BE1_CFG_REG 0x108 +#define FE0_CFG_REG 0x10c +#define FE1_CFG_REG 0x110 +#define MP_CFG_REG 0x114 +#define LCD0CH0_CFG_REG 0x118 +#define LCD1CH0_CFG_REG 0x11c +#define LCD0CH1_CFG_REG 0x12c +#define LCD1CH1_CFG_REG 0x130 #define CSI_CFG_REG 0x134 #define VE_CFG_REG 0x13c #define AUDIO_CODEC_SCLK_CFG_REG 0x140 +#define HDMI_CLOCK_CFG_REG 0x150 #define MALI_CLOCK_CFG_REG 0x154 #define IEP_SCLK_CFG_REG 0x160 @@ -87,6 +99,13 @@ static struct sunxi_ccu_reset sun4i_a10_ SUNXI_CCU_RESET(A10_RST_USB_PHY0, USBPHY_CFG_REG, 0), SUNXI_CCU_RESET(A10_RST_USB_PHY1, USBPHY_CFG_REG, 1), SUNXI_CCU_RESET(A10_RST_USB_PHY2, USBPHY_CFG_REG, 2), + SUNXI_CCU_RESET(A10_RST_DE_BE0, BE0_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_DE_BE1, BE1_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_DE_FE0, FE0_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_DE_FE1, FE1_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_DE_MP, MP_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_TCON0, LCD0CH0_CFG_REG, 30), + SUNXI_CCU_RESET(A10_RST_TCON1, LCD1CH0_CFG_REG, 30), }; static const char *cpu_parents[] = { "losc", "osc24m", "pll_core", "pll_periph" }; @@ -94,8 +113,13 @@ static const char *axi_parents[] = { "cp static const char *ahb_parents[] = { "axi", "pll_periph", "pll_periph_base" }; static const char *apb0_parents[] = { "ahb" }; static const char *apb1_parents[] = { "osc24m", "pll_periph", "losc" }; -static const char *mod_parents[] = { "osc24m", "pll_periph", "pll_ddr" }; +static const char *mod_parents[] = { "osc24m", "pll_periph", "pll_ddr_other" }; static const char *sata_parents[] = { "pll6_periph_sata", "external" }; +static const char *de_parents[] = { "pll_video0", "pll_video1", "pll_ddr_other" }; +static const char *lcd0_parents[] = { "pll_video0", "pll_video1", "pll_video0x2" }; +static const char *lcd1_parents[] = { "pll_video0", "pll_video1", "pll_video0x2", "pll_video1x2" }; +static const char *lcd0ch1c2[] = { "tcon0-ch1-clk2" }; +static const char *lcd1ch1c2[] = { "tcon1-ch1-clk2" }; static const struct sunxi_ccu_nkmp_tbl sun4i_a10_pll1_table[] = { { 1008000000, 21, 1, 0, 0 }, @@ -170,6 +194,24 @@ static struct sunxi_ccu_clk sun4i_a10_cc __BIT(31), /* enable */ 0), + SUNXI_CCU_NKMP(A10_CLK_PLL_DDR_BASE, "pll_ddr_other", "osc24m", + PLL5_CFG_REG, /* reg */ + __BITS(12, 8), /* n */ + __BITS(5,4), /* k */ + 0, /* m */ + __BITS(17,16), /* p */ + __BIT(31), /* enable */ + SUNXI_CCU_NKMP_FACTOR_N_EXACT | SUNXI_CCU_NKMP_FACTOR_P_POW2), + + SUNXI_CCU_NKMP(A10_CLK_PLL_DDR, "pll_ddr", "osc24m", + PLL5_CFG_REG, /* reg */ + __BITS(12, 8), /* n */ + __BITS(5,4), /* k */ + __BITS(1,0), /* m */ + 0, /* p */ + __BIT(31), /* enable */ + SUNXI_CCU_NKMP_FACTOR_N_EXACT), + SUNXI_CCU_DIV(A10_CLK_CPU, "cpu", cpu_parents, CPU_AHB_APB0_CFG_REG, /* reg */ 0, /* div */ @@ -255,6 +297,113 @@ static struct sunxi_ccu_clk sun4i_a10_cc SUNXI_CCU_PHASE(A10_CLK_MMC3_OUTPUT, "mmc3_output", "mmc3", SD3_SCLK_CFG_REG, __BITS(10,8)), + SUNXI_CCU_FRACTIONAL(A10_CLK_PLL_VIDEO0, "pll_video0", "osc24m", + PLL3_CFG_REG, /* reg */ + __BITS(7,0), /* m */ + 9, /* m_min */ + 127, /* m_max */ + __BIT(15), /* frac_en */ + __BIT(14), /* frac_sel */ + 270000000, 297000000, /* frac values */ + 8, /* prediv */ + __BIT(31) /* enable */ + ), + SUNXI_CCU_FRACTIONAL(A10_CLK_PLL_VIDEO1, "pll_video1", "osc24m", + PLL7_CFG_REG, /* reg */ + __BITS(7,0), /* m */ + 9, /* m_min */ + 127, /* m_max */ + __BIT(15), /* frac_en */ + __BIT(14), /* frac_sel */ + 270000000, 297000000, /* frac values */ + 8, /* prediv */ + __BIT(31) /* enable */ + ), + SUNXI_CCU_FIXED_FACTOR(A10_CLK_PLL_VIDEO0_2X, + "pll_video0x2", "pll_video0", + 1, 2), + SUNXI_CCU_FIXED_FACTOR(A10_CLK_PLL_VIDEO1_2X, + "pll_video1x2", "pll_video1", + 1, 2), + + SUNXI_CCU_DIV_GATE(A10_CLK_DE_BE0, "debe0-mod", de_parents, + BE0_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + 0 /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_DE_BE1, "debe1-mod", de_parents, + BE1_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + 0 /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_DE_FE0, "defe0-mod", de_parents, + FE0_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + 0 /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_DE_FE1, "defe1-mod", de_parents, + FE1_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + 0 /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH0, "tcon0-ch0", lcd0_parents, + LCD0CH0_CFG_REG, /* reg */ + 0, /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH0, "tcon1-ch0", lcd1_parents, + LCD1CH0_CFG_REG, /* reg */ + 0, /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH1_SCLK2, "tcon0-ch1-clk2", lcd1_parents, + LCD0CH1_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON0_CH1, "tcon0-ch1", lcd0ch1c2, + LCD0CH1_CFG_REG, /* reg */ + __BIT(11), /* div */ + 0, /* sel */ + __BIT(15), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH1_SCLK2, "tcon1-ch1-clk2", lcd1_parents, + LCD1CH1_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_TCON1_CH1, "tcon1-ch1", lcd1ch1c2, + LCD1CH1_CFG_REG, /* reg */ + __BIT(11), /* div */ + 0, /* sel */ + __BIT(15), /* enable */ + SUNXI_CCU_DIV_SET_RATE_PARENT /* flags */ + ), + SUNXI_CCU_DIV_GATE(A10_CLK_HDMI, "hdmi-mod", lcd1_parents, + HDMI_CLOCK_CFG_REG, /* reg */ + __BITS(3,0), /* div */ + __BITS(25,24), /* sel */ + __BIT(31), /* enable */ + 0 /* flags */ + ), + /* AHB_GATING_REG0 */ SUNXI_CCU_GATE(A10_CLK_AHB_OTG, "ahb-otg", "ahb", AHB_GATING_REG0, 0), @@ -399,6 +548,16 @@ static struct sunxi_ccu_clk sun4i_a10_cc SUNXI_CCU_GATE(A10_CLK_APB1_UART7, "apb1-uart7", "apb1", APB1_GATING_REG, 23), + /* DRAM GATING */ + SUNXI_CCU_GATE(A10_CLK_DRAM_DE_BE0, "dram-de-be0", "pll_ddr_other", + DRAM_GATING_REG, 26), + SUNXI_CCU_GATE(A10_CLK_DRAM_DE_BE1, "dram-de-be1", "pll_ddr_other", + DRAM_GATING_REG, 27), + SUNXI_CCU_GATE(A10_CLK_DRAM_DE_FE0, "dram-de-fe0", "pll_ddr_other", + DRAM_GATING_REG, 25), + SUNXI_CCU_GATE(A10_CLK_DRAM_DE_FE1, "dram-de-fe1", "pll_ddr_other", + DRAM_GATING_REG, 24), + /* AUDIO_CODEC_SCLK_CFG_REG */ SUNXI_CCU_GATE(A10_CLK_CODEC, "codec", "pll_audio", AUDIO_CODEC_SCLK_CFG_REG, 31), Index: src/sys/arch/arm/sunxi/sunxi_ccu.c diff -u src/sys/arch/arm/sunxi/sunxi_ccu.c:1.7 src/sys/arch/arm/sunxi/sunxi_ccu.c:1.8 --- src/sys/arch/arm/sunxi/sunxi_ccu.c:1.7 Sat Sep 30 12:48:58 2017 +++ src/sys/arch/arm/sunxi/sunxi_ccu.c Mon Mar 19 16:18:30 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.c,v 1.7 2017/09/30 12:48:58 jmcneill Exp $ */ +/* $NetBSD: sunxi_ccu.c,v 1.8 2018/03/19 16:18:30 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -31,7 +31,7 @@ #include "opt_fdt_arm.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu.c,v 1.7 2017/09/30 12:48:58 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu.c,v 1.8 2018/03/19 16:18:30 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -331,6 +331,7 @@ sunxi_ccu_print(struct sunxi_ccu_softc * case SUNXI_CCU_DIV: type = "div"; break; case SUNXI_CCU_PHASE: type = "phase"; break; case SUNXI_CCU_FIXED_FACTOR: type = "fixed-factor"; break; + case SUNXI_CCU_FRACTIONAL: type = "fractional"; break; default: type = "???"; break; } Index: src/sys/arch/arm/sunxi/sunxi_ccu.h diff -u src/sys/arch/arm/sunxi/sunxi_ccu.h:1.15 src/sys/arch/arm/sunxi/sunxi_ccu.h:1.16 --- src/sys/arch/arm/sunxi/sunxi_ccu.h:1.15 Sat Oct 28 13:13:45 2017 +++ src/sys/arch/arm/sunxi/sunxi_ccu.h Mon Mar 19 16:18:30 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.h,v 1.15 2017/10/28 13:13:45 jmcneill Exp $ */ +/* $NetBSD: sunxi_ccu.h,v 1.16 2018/03/19 16:18:30 bouyer Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -63,6 +63,7 @@ enum sunxi_ccu_clktype { SUNXI_CCU_DIV, SUNXI_CCU_PHASE, SUNXI_CCU_FIXED_FACTOR, + SUNXI_CCU_FRACTIONAL, }; struct sunxi_ccu_gate { @@ -342,6 +343,50 @@ const char *sunxi_ccu_fixed_factor_get_p .get_parent = sunxi_ccu_fixed_factor_get_parent, \ } +struct sunxi_ccu_fractional { + bus_size_t reg; + const char *parent; + uint32_t m; + uint32_t m_min; + uint32_t m_max; + uint32_t frac_en; + uint32_t frac_sel; + uint32_t frac[2]; + uint32_t prediv; + uint32_t enable; +}; + +int sunxi_ccu_fractional_enable(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, int); +u_int sunxi_ccu_fractional_get_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *); +int sunxi_ccu_fractional_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +const char *sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *); + +#define SUNXI_CCU_FRACTIONAL(_id, _name, _parent, _reg, _m, _m_min, _m_max, \ + _frac_en, _frac_sel, _frac0, _frac1, _prediv, _enable) \ + [_id] = { \ + .type = SUNXI_CCU_FRACTIONAL, \ + .base.name = (_name), \ + .u.fractional.reg = (_reg), \ + .u.fractional.parent = (_parent), \ + .u.fractional.m = (_m), \ + .u.fractional.m_min = (_m_min), \ + .u.fractional.m_max = (_m_max), \ + .u.fractional.prediv = (_prediv), \ + .u.fractional.frac_en = (_frac_en), \ + .u.fractional.frac_sel = (_frac_sel), \ + .u.fractional.frac[0] = (_frac0), \ + .u.fractional.frac[1] = (_frac1), \ + .u.fractional.enable = (_enable), \ + .enable = sunxi_ccu_fractional_enable, \ + .get_rate = sunxi_ccu_fractional_get_rate, \ + .set_rate = sunxi_ccu_fractional_set_rate, \ + .get_parent = sunxi_ccu_fractional_get_parent, \ + } + struct sunxi_ccu_clk { struct clk base; enum sunxi_ccu_clktype type; @@ -353,6 +398,7 @@ struct sunxi_ccu_clk { struct sunxi_ccu_div div; struct sunxi_ccu_phase phase; struct sunxi_ccu_fixed_factor fixed_factor; + struct sunxi_ccu_fractional fractional; } u; int (*enable)(struct sunxi_ccu_softc *, Added files: Index: src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c:1.1 --- /dev/null Mon Mar 19 16:18:31 2018 +++ src/sys/arch/arm/sunxi/sunxi_ccu_fractional.c Mon Mar 19 16:18:30 2018 @@ -0,0 +1,166 @@ +/* $NetBSD: sunxi_ccu_fractional.c,v 1.1 2018/03/19 16:18:30 bouyer Exp $ */ + +/*- + * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_fractional.c,v 1.1 2018/03/19 16:18:30 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <arm/sunxi/sunxi_ccu.h> + +int +sunxi_ccu_fractional_enable(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, int enable) +{ + struct sunxi_ccu_fractional *fractional = &clk->u.fractional; + uint32_t val; + + KASSERT(clk->type == SUNXI_CCU_FRACTIONAL); + + if (!fractional->enable) + return enable ? 0 : EINVAL; + + val = CCU_READ(sc, fractional->reg); + if (enable) + val |= fractional->enable; + else + val &= ~fractional->enable; + CCU_WRITE(sc, fractional->reg, val); + + return 0; +} + +u_int +sunxi_ccu_fractional_get_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk) +{ + struct sunxi_ccu_fractional *fractional = &clk->u.fractional; + struct clk *clkp, *clkp_parent; + u_int rate, m; + uint32_t val; + + KASSERT(clk->type == SUNXI_CCU_FRACTIONAL); + + clkp = &clk->base; + clkp_parent = clk_get_parent(clkp); + if (clkp_parent == NULL) + return 0; + + rate = clk_get_rate(clkp_parent); + if (rate == 0) + return 0; + + if (fractional->prediv > 0) + rate = rate / fractional->prediv; + + val = CCU_READ(sc, fractional->reg); + + if (fractional->enable && !(val & fractional->enable)) + return 0; + + if (val & fractional->frac_en) { + int sel = __SHIFTOUT(val, fractional->frac_sel); + return fractional->frac[sel]; + } + m = __SHIFTOUT(val, fractional->m); + + return rate * m; +} + +int +sunxi_ccu_fractional_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, u_int new_rate) +{ + struct sunxi_ccu_fractional *fractional = &clk->u.fractional; + struct clk *clkp, *clkp_parent; + u_int parent_rate, best_rate, best_m; + u_int m, rate; + int best_diff; + uint32_t val; + int i; + + clkp = &clk->base; + clkp_parent = clk_get_parent(clkp); + if (clkp_parent == NULL) + return ENXIO; + + parent_rate = clk_get_rate(clkp_parent); + if (parent_rate == 0) + return (new_rate == 0) ? 0 : ERANGE; + + if (fractional->prediv > 0) + parent_rate = parent_rate / fractional->prediv; + + val = CCU_READ(sc, fractional->reg); + for (i = 0; i < __arraycount(fractional->frac); i++) { + if (fractional->frac[i] == new_rate) { + val |= fractional->frac_en; + val &= ~fractional->frac_sel; + val |= __SHIFTIN(i, fractional->frac_sel); + CCU_WRITE(sc, fractional->reg, val); + return 0; + } + } + val &= ~fractional->frac_en; + + best_rate = 0; + best_diff = INT_MAX; + + for (m = fractional->m_min; m <= fractional->m_max; m++) { + rate = parent_rate * m; + const int diff = abs(new_rate - rate); + if (diff < best_diff) { + best_diff = diff; + best_rate = rate; + best_m = m; + } + } + + if (best_rate == 0) + return ERANGE; + + val &= ~fractional->m; + val |= __SHIFTIN(best_m, fractional->m); + CCU_WRITE(sc, fractional->reg, val); + + return 0; +} + +const char * +sunxi_ccu_fractional_get_parent(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk) +{ + struct sunxi_ccu_fractional *fractional = &clk->u.fractional; + + KASSERT(clk->type == SUNXI_CCU_FRACTIONAL); + + return fractional->parent; +}