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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
+ * 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;
+}