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;
+}

Reply via email to