Module Name: src Committed By: snj Date: Tue Jul 25 02:03:17 UTC 2017
Modified Files: src/sys/arch/arm/sunxi [netbsd-8]: files.sunxi sun8i_h3_ccu.c sunxi_ccu.c sunxi_ccu.h sunxi_mmc.c sunxi_mmc.h Added Files: src/sys/arch/arm/sunxi [netbsd-8]: sunxi_ccu_phase.c Log Message: Pull up following revision(s) (requested by jmcneill in ticket #143): sys/arch/arm/sunxi/files.sunxi: revision 1.12 sys/arch/arm/sunxi/sun8i_h3_ccu.c: revision 1.8 sys/arch/arm/sunxi/sunxi_ccu.c: revision 1.6 sys/arch/arm/sunxi/sunxi_ccu.h: revision 1.7 sys/arch/arm/sunxi/sunxi_ccu_phase.c: revision 1.1 sys/arch/arm/sunxi/sunxi_mmc.c: revision 1.3 sys/arch/arm/sunxi/sunxi_mmc.h: revision 1.2 Add SDMMC[012] sample/output clock phase controls. -- Add support for eMMC DDR52 transfer mode. To generate a diff of this commit: cvs rdiff -u -r1.12.4.2 -r1.12.4.3 src/sys/arch/arm/sunxi/files.sunxi cvs rdiff -u -r1.8.4.2 -r1.8.4.3 src/sys/arch/arm/sunxi/sun8i_h3_ccu.c cvs rdiff -u -r1.6.4.2 -r1.6.4.3 src/sys/arch/arm/sunxi/sunxi_ccu.c cvs rdiff -u -r1.7.4.2 -r1.7.4.3 src/sys/arch/arm/sunxi/sunxi_ccu.h cvs rdiff -u -r0 -r1.1.4.2 src/sys/arch/arm/sunxi/sunxi_ccu_phase.c cvs rdiff -u -r1.3.4.3 -r1.3.4.4 src/sys/arch/arm/sunxi/sunxi_mmc.c cvs rdiff -u -r1.2.4.2 -r1.2.4.3 src/sys/arch/arm/sunxi/sunxi_mmc.h 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.12.4.2 src/sys/arch/arm/sunxi/files.sunxi:1.12.4.3 --- src/sys/arch/arm/sunxi/files.sunxi:1.12.4.2 Tue Jul 18 19:13:08 2017 +++ src/sys/arch/arm/sunxi/files.sunxi Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -# $NetBSD: files.sunxi,v 1.12.4.2 2017/07/18 19:13:08 snj Exp $ +# $NetBSD: files.sunxi,v 1.12.4.3 2017/07/25 02:03:16 snj Exp $ # # Configuration info for Allwinner sunxi family SoCs # @@ -24,6 +24,7 @@ file arch/arm/sunxi/sunxi_ccu_div.c sun 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 +file arch/arm/sunxi/sunxi_ccu_phase.c sunxi_ccu file arch/arm/sunxi/sunxi_ccu_prediv.c sunxi_ccu # CCU (A31) Index: src/sys/arch/arm/sunxi/sun8i_h3_ccu.c diff -u src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.2 src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.3 --- src/sys/arch/arm/sunxi/sun8i_h3_ccu.c:1.8.4.2 Tue Jul 18 19:13:08 2017 +++ src/sys/arch/arm/sunxi/sun8i_h3_ccu.c Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sun8i_h3_ccu.c,v 1.8.4.2 2017/07/18 19:13:08 snj Exp $ */ +/* $NetBSD: sun8i_h3_ccu.c,v 1.8.4.3 2017/07/25 02:03:16 snj Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu.c,v 1.8.4.2 2017/07/18 19:13:08 snj Exp $"); +__KERNEL_RCSID(1, "$NetBSD: sun8i_h3_ccu.c,v 1.8.4.3 2017/07/25 02:03:16 snj Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -184,12 +184,24 @@ static struct sunxi_ccu_clk sun8i_h3_ccu SUNXI_CCU_NM(H3_CLK_MMC0, "mmc0", mod_parents, SDMMC0_CLK_REG, __BITS(17, 16), __BITS(3,0), __BITS(25, 24), __BIT(31), SUNXI_CCU_NM_POWER_OF_TWO|SUNXI_CCU_NM_ROUND_DOWN), + SUNXI_CCU_PHASE(H3_CLK_MMC0_SAMPLE, "mmc0_sample", "mmc0", + SDMMC0_CLK_REG, __BITS(22,20)), + SUNXI_CCU_PHASE(H3_CLK_MMC0_OUTPUT, "mmc0_output", "mmc0", + SDMMC0_CLK_REG, __BITS(10,8)), SUNXI_CCU_NM(H3_CLK_MMC1, "mmc1", mod_parents, SDMMC1_CLK_REG, __BITS(17, 16), __BITS(3,0), __BITS(25, 24), __BIT(31), SUNXI_CCU_NM_POWER_OF_TWO|SUNXI_CCU_NM_ROUND_DOWN), + SUNXI_CCU_PHASE(H3_CLK_MMC1_SAMPLE, "mmc1_sample", "mmc1", + SDMMC1_CLK_REG, __BITS(22,20)), + SUNXI_CCU_PHASE(H3_CLK_MMC1_OUTPUT, "mmc1_output", "mmc1", + SDMMC1_CLK_REG, __BITS(10,8)), SUNXI_CCU_NM(H3_CLK_MMC2, "mmc2", mod_parents, SDMMC2_CLK_REG, __BITS(17, 16), __BITS(3,0), __BITS(25, 24), __BIT(31), SUNXI_CCU_NM_POWER_OF_TWO|SUNXI_CCU_NM_ROUND_DOWN), + SUNXI_CCU_PHASE(H3_CLK_MMC2_SAMPLE, "mmc2_sample", "mmc2", + SDMMC2_CLK_REG, __BITS(22,20)), + SUNXI_CCU_PHASE(H3_CLK_MMC2_OUTPUT, "mmc2_output", "mmc2", + SDMMC2_CLK_REG, __BITS(10,8)), SUNXI_CCU_GATE(H3_CLK_BUS_MMC0, "bus-mmc0", "ahb1", BUS_CLK_GATING_REG0, 8), Index: src/sys/arch/arm/sunxi/sunxi_ccu.c diff -u src/sys/arch/arm/sunxi/sunxi_ccu.c:1.6.4.2 src/sys/arch/arm/sunxi/sunxi_ccu.c:1.6.4.3 --- src/sys/arch/arm/sunxi/sunxi_ccu.c:1.6.4.2 Tue Jul 18 19:13:08 2017 +++ src/sys/arch/arm/sunxi/sunxi_ccu.c Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.c,v 1.6.4.2 2017/07/18 19:13:08 snj Exp $ */ +/* $NetBSD: sunxi_ccu.c,v 1.6.4.3 2017/07/25 02:03:16 snj 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.6.4.2 2017/07/18 19:13:08 snj Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu.c,v 1.6.4.3 2017/07/25 02:03:16 snj Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -329,6 +329,7 @@ sunxi_ccu_print(struct sunxi_ccu_softc * case SUNXI_CCU_NKMP: type = "nkmp"; break; case SUNXI_CCU_PREDIV: type = "prediv"; break; case SUNXI_CCU_DIV: type = "div"; break; + case SUNXI_CCU_PHASE: type = "phase"; break; default: type = "???"; break; } Index: src/sys/arch/arm/sunxi/sunxi_ccu.h diff -u src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.2 src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.3 --- src/sys/arch/arm/sunxi/sunxi_ccu.h:1.7.4.2 Tue Jul 18 19:13:08 2017 +++ src/sys/arch/arm/sunxi/sunxi_ccu.h Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_ccu.h,v 1.7.4.2 2017/07/18 19:13:08 snj Exp $ */ +/* $NetBSD: sunxi_ccu.h,v 1.7.4.3 2017/07/25 02:03:16 snj Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -61,6 +61,7 @@ enum sunxi_ccu_clktype { SUNXI_CCU_NKMP, SUNXI_CCU_PREDIV, SUNXI_CCU_DIV, + SUNXI_CCU_PHASE, }; struct sunxi_ccu_gate { @@ -252,6 +253,31 @@ const char *sunxi_ccu_prediv_get_parent( .get_parent = sunxi_ccu_prediv_get_parent, \ } +struct sunxi_ccu_phase { + bus_size_t reg; + const char *parent; + uint32_t mask; +}; + +u_int sunxi_ccu_phase_get_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *); +int sunxi_ccu_phase_set_rate(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *, u_int); +const char *sunxi_ccu_phase_get_parent(struct sunxi_ccu_softc *, + struct sunxi_ccu_clk *); + +#define SUNXI_CCU_PHASE(_id, _name, _parent, _reg, _mask) \ + [_id] = { \ + .type = SUNXI_CCU_PHASE, \ + .base.name = (_name), \ + .u.phase.reg = (_reg), \ + .u.phase.parent = (_parent), \ + .u.phase.mask = (_mask), \ + .get_rate = sunxi_ccu_phase_get_rate, \ + .set_rate = sunxi_ccu_phase_set_rate, \ + .get_parent = sunxi_ccu_phase_get_parent, \ + } + struct sunxi_ccu_clk { struct clk base; enum sunxi_ccu_clktype type; @@ -261,6 +287,7 @@ struct sunxi_ccu_clk { struct sunxi_ccu_nkmp nkmp; struct sunxi_ccu_prediv prediv; struct sunxi_ccu_div div; + struct sunxi_ccu_phase phase; } u; int (*enable)(struct sunxi_ccu_softc *, Index: src/sys/arch/arm/sunxi/sunxi_mmc.c diff -u src/sys/arch/arm/sunxi/sunxi_mmc.c:1.3.4.3 src/sys/arch/arm/sunxi/sunxi_mmc.c:1.3.4.4 --- src/sys/arch/arm/sunxi/sunxi_mmc.c:1.3.4.3 Tue Jul 25 01:49:13 2017 +++ src/sys/arch/arm/sunxi/sunxi_mmc.c Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_mmc.c,v 1.3.4.3 2017/07/25 01:49:13 snj Exp $ */ +/* $NetBSD: sunxi_mmc.c,v 1.3.4.4 2017/07/25 02:03:16 snj Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.3.4.3 2017/07/25 01:49:13 snj Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.3.4.4 2017/07/25 02:03:16 snj Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -45,6 +45,27 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c, #include <arm/sunxi/sunxi_mmc.h> +enum sunxi_mmc_timing { + SUNXI_MMC_TIMING_400K, + SUNXI_MMC_TIMING_25M, + SUNXI_MMC_TIMING_50M, + SUNXI_MMC_TIMING_50M_DDR, + SUNXI_MMC_TIMING_50M_DDR_8BIT, +}; + +struct sunxi_mmc_delay { + u_int output_phase; + u_int sample_phase; +}; + +static const struct sunxi_mmc_delay sunxi_mmc_delays[] = { + [SUNXI_MMC_TIMING_400K] = { 180, 180 }, + [SUNXI_MMC_TIMING_25M] = { 180, 75 }, + [SUNXI_MMC_TIMING_50M] = { 90, 120 }, + [SUNXI_MMC_TIMING_50M_DDR] = { 60, 120 }, + [SUNXI_MMC_TIMING_50M_DDR_8BIT] = { 90, 180 }, +}; + #define SUNXI_MMC_NDESC 16 #define SUNXI_MMC_DMA_XFERLEN 0x10000 #define SUNXI_MMC_DMA_FTRGLEVEL 0x20070008 @@ -64,9 +85,10 @@ static int sunxi_mmc_host_maxblklen(sdmm static int sunxi_mmc_card_detect(sdmmc_chipset_handle_t); static int sunxi_mmc_write_protect(sdmmc_chipset_handle_t); static int sunxi_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t); -static int sunxi_mmc_bus_clock(sdmmc_chipset_handle_t, int); +static int sunxi_mmc_bus_clock(sdmmc_chipset_handle_t, int, bool); static int sunxi_mmc_bus_width(sdmmc_chipset_handle_t, int); static int sunxi_mmc_bus_rod(sdmmc_chipset_handle_t, int); +static int sunxi_mmc_signal_voltage(sdmmc_chipset_handle_t, int); static void sunxi_mmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); static void sunxi_mmc_card_enable_intr(sdmmc_chipset_handle_t, int); @@ -79,9 +101,10 @@ static struct sdmmc_chip_functions sunxi .card_detect = sunxi_mmc_card_detect, .write_protect = sunxi_mmc_write_protect, .bus_power = sunxi_mmc_bus_power, - .bus_clock = sunxi_mmc_bus_clock, + .bus_clock_ddr = sunxi_mmc_bus_clock, .bus_width = sunxi_mmc_bus_width, .bus_rod = sunxi_mmc_bus_rod, + .signal_voltage = sunxi_mmc_signal_voltage, .exec_command = sunxi_mmc_exec_command, .card_enable_intr = sunxi_mmc_card_enable_intr, .card_intr_ack = sunxi_mmc_card_intr_ack, @@ -129,6 +152,8 @@ struct sunxi_mmc_softc { int sc_gpio_cd_inverted; struct fdtbus_gpio_pin *sc_gpio_wp; int sc_gpio_wp_inverted; + + struct fdtbus_regulator *sc_reg_vqmmc; }; CFATTACH_DECL_NEW(sunxi_mmc, sizeof(struct sunxi_mmc_softc), @@ -188,6 +213,8 @@ sunxi_mmc_attach(device_t parent, device return; } + sc->sc_reg_vqmmc = fdtbus_regulator_acquire(phandle, "vqmmc-supply"); + if (clk_enable(sc->sc_clk_ahb) != 0 || clk_enable(sc->sc_clk_mmc) != 0) { aprint_error(": couldn't enable clocks\n"); @@ -287,9 +314,44 @@ free: } static int -sunxi_mmc_set_clock(struct sunxi_mmc_softc *sc, u_int freq) +sunxi_mmc_set_clock(struct sunxi_mmc_softc *sc, u_int freq, bool ddr) { - return clk_set_rate(sc->sc_clk_mmc, freq * 1000); + const struct sunxi_mmc_delay *delays; + int error, timing; + + if (freq <= 400) { + timing = SUNXI_MMC_TIMING_400K; + } else if (freq <= 25000) { + timing = SUNXI_MMC_TIMING_25M; + } else if (freq <= 52000) { + if (ddr) { + timing = sc->sc_mmc_width == 8 ? + SUNXI_MMC_TIMING_50M_DDR_8BIT : + SUNXI_MMC_TIMING_50M_DDR; + } else { + timing = SUNXI_MMC_TIMING_50M; + } + } else + return EINVAL; + + delays = &sunxi_mmc_delays[timing]; + + error = clk_set_rate(sc->sc_clk_mmc, (freq * 1000) << ddr); + if (error != 0) + return error; + + if (sc->sc_clk_sample) { + error = clk_set_rate(sc->sc_clk_sample, delays->sample_phase); + if (error != 0) + return error; + } + if (sc->sc_clk_output) { + error = clk_set_rate(sc->sc_clk_output, delays->output_phase); + if (error != 0) + return error; + } + + return 0; } static void @@ -301,7 +363,7 @@ sunxi_mmc_attach_i(device_t self) sunxi_mmc_host_reset(sc); sunxi_mmc_bus_width(sc, 1); - sunxi_mmc_set_clock(sc, 400); + sunxi_mmc_set_clock(sc, 400, false); if (of_getprop_uint32(sc->sc_phandle, "bus-width", &width) != 0) width = 4; @@ -318,6 +380,7 @@ sunxi_mmc_attach_i(device_t self) SMC_CAPS_AUTO_STOP | SMC_CAPS_SD_HIGHSPEED | SMC_CAPS_MMC_HIGHSPEED | + SMC_CAPS_MMC_DDR52 | SMC_CAPS_POLLING; if (width == 4) saa.saa_caps |= SMC_CAPS_4BIT_MODE; @@ -535,10 +598,10 @@ sunxi_mmc_update_clock(struct sunxi_mmc_ } static int -sunxi_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +sunxi_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, bool ddr) { struct sunxi_mmc_softc *sc = sch; - uint32_t clkcr; + uint32_t clkcr, gctrl; clkcr = MMC_READ(sc, SUNXI_MMC_CLKCR); if (clkcr & SUNXI_MMC_CLKCR_CARDCLKON) { @@ -551,11 +614,19 @@ sunxi_mmc_bus_clock(sdmmc_chipset_handle if (freq) { clkcr &= ~SUNXI_MMC_CLKCR_DIV; + clkcr |= __SHIFTIN(ddr, SUNXI_MMC_CLKCR_DIV); MMC_WRITE(sc, SUNXI_MMC_CLKCR, clkcr); if (sunxi_mmc_update_clock(sc) != 0) return 1; - if (sunxi_mmc_set_clock(sc, freq) != 0) + gctrl = MMC_READ(sc, SUNXI_MMC_GCTRL); + if (ddr) + gctrl |= SUNXI_MMC_GCTRL_DDR_MODE; + else + gctrl &= ~SUNXI_MMC_GCTRL_DDR_MODE; + MMC_WRITE(sc, SUNXI_MMC_GCTRL, gctrl); + + if (sunxi_mmc_set_clock(sc, freq, ddr) != 0) return 1; clkcr |= SUNXI_MMC_CLKCR_CARDCLKON; @@ -602,6 +673,34 @@ sunxi_mmc_bus_rod(sdmmc_chipset_handle_t } static int +sunxi_mmc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage) +{ + struct sunxi_mmc_softc *sc = sch; + u_int uvol; + int error; + + if (sc->sc_reg_vqmmc == NULL) + return 0; + + switch (signal_voltage) { + case SDMMC_SIGNAL_VOLTAGE_330: + uvol = 3300000; + break; + case SDMMC_SIGNAL_VOLTAGE_180: + uvol = 1800000; + break; + default: + return EINVAL; + } + + error = fdtbus_regulator_set_voltage(sc->sc_reg_vqmmc, uvol, uvol); + if (error != 0) + return error; + + return fdtbus_regulator_enable(sc->sc_reg_vqmmc); +} + +static int sunxi_mmc_dma_prepare(struct sunxi_mmc_softc *sc, struct sdmmc_command *cmd) { struct sunxi_mmc_idma_descriptor *dma = sc->sc_idma_desc; Index: src/sys/arch/arm/sunxi/sunxi_mmc.h diff -u src/sys/arch/arm/sunxi/sunxi_mmc.h:1.2.4.2 src/sys/arch/arm/sunxi/sunxi_mmc.h:1.2.4.3 --- src/sys/arch/arm/sunxi/sunxi_mmc.h:1.2.4.2 Tue Jul 18 19:13:08 2017 +++ src/sys/arch/arm/sunxi/sunxi_mmc.h Tue Jul 25 02:03:16 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_mmc.h,v 1.2.4.2 2017/07/18 19:13:08 snj Exp $ */ +/* $NetBSD: sunxi_mmc.h,v 1.2.4.3 2017/07/25 02:03:16 snj Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -51,6 +51,7 @@ #define SUNXI_MMC_BBCR 0x004C #define SUNXI_MMC_DBGC 0x0050 #define SUNXI_MMC_A12A 0x0058 /* A80 */ +#define SUNXI_MMC_NTSR 0x005C #define SUNXI_MMC_HWRST 0x0078 /* A80 */ #define SUNXI_MMC_DMAC 0x0080 #define SUNXI_MMC_DLBA 0x0084 @@ -58,7 +59,7 @@ #define SUNXI_MMC_IDIE 0x008C #define SUNXI_MMC_CHDA 0x0090 #define SUNXI_MMC_CBDA 0x0094 -#define SUNXI_MMC_FIFO 0x0100 + #define SUNXI_MMC_GCTRL_ACCESS_BY_AHB __BIT(31) #define SUNXI_MMC_GCTRL_WAIT_MEM_ACCESS_DONE __BIT(30) #define SUNXI_MMC_GCTRL_DDR_MODE __BIT(10) @@ -73,7 +74,7 @@ SUNXI_MMC_GCTRL_DMARESET) #define SUNXI_MMC_CLKCR_LOWPOWERON __BIT(17) #define SUNXI_MMC_CLKCR_CARDCLKON __BIT(16) -#define SUNXI_MMC_CLKCR_DIV __BITS(15,0) +#define SUNXI_MMC_CLKCR_DIV __BITS(7,0) #define SUNXI_MMC_WIDTH_1 0 #define SUNXI_MMC_WIDTH_4 1 #define SUNXI_MMC_WIDTH_8 2 @@ -137,6 +138,9 @@ #define SUNXI_MMC_FUNCSEL_ABT_RD_DATA __BIT(2) #define SUNXI_MMC_FUNCSEL_SDIO_RD_WAIT __BIT(1) #define SUNXI_MMC_FUNCSEL_SEND_IRQ_RSP __BIT(0) +#define SUNXI_MMC_NTSR_MODE_SELECT __BIT(31) +#define SUNXI_MMC_NTSR_SAMPLE_PHASE __BITS(30,6) +#define SUNXI_MMC_NTSR_OUTPUT_PHASE __BITS(1,0) #define SUNXI_MMC_DMAC_REFETCH_DES __BIT(31) #define SUNXI_MMC_DMAC_IDMA_ON __BIT(7) #define SUNXI_MMC_DMAC_FIX_BURST __BIT(1) Added files: Index: src/sys/arch/arm/sunxi/sunxi_ccu_phase.c diff -u /dev/null src/sys/arch/arm/sunxi/sunxi_ccu_phase.c:1.1.4.2 --- /dev/null Tue Jul 25 02:03:17 2017 +++ src/sys/arch/arm/sunxi/sunxi_ccu_phase.c Tue Jul 25 02:03:16 2017 @@ -0,0 +1,126 @@ +/* $NetBSD: sunxi_ccu_phase.c,v 1.1.4.2 2017/07/25 02:03:16 snj 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_phase.c,v 1.1.4.2 2017/07/25 02:03:16 snj Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <arm/sunxi/sunxi_ccu.h> + +static u_int +sunxi_ccu_phase_get_parent_rate(struct clk *clkp) +{ + struct clk *clkp_parent; + + clkp_parent = clk_get_parent(clkp); + if (clkp_parent == NULL) + return 0; + + return clk_get_rate(clkp_parent); +} + +static u_int +sunxi_ccu_phase_div(u_int n, u_int d) +{ + return (n + (d/2)) / d; +} + +u_int +sunxi_ccu_phase_get_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk) +{ + struct sunxi_ccu_phase *phase = &clk->u.phase; + struct clk *clkp = &clk->base; + u_int p_rate, gp_rate, p_div, delay; + uint32_t val; + + KASSERT(clk->type == SUNXI_CCU_PHASE); + + p_rate = sunxi_ccu_phase_get_parent_rate(clkp); + if (p_rate == 0) + return 0; + gp_rate = sunxi_ccu_phase_get_parent_rate(clk_get_parent(clkp)); + if (gp_rate == 0) + return 0; + + p_div = gp_rate / p_rate; + + val = CCU_READ(sc, phase->reg); + delay = __SHIFTOUT(val, phase->mask); + + return delay * sunxi_ccu_phase_div(360, p_div); +} + +int +sunxi_ccu_phase_set_rate(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk, u_int new_rate) +{ + struct sunxi_ccu_phase *phase = &clk->u.phase; + struct clk *clkp = &clk->base; + u_int p_rate, gp_rate, p_div, delay; + uint32_t val; + + KASSERT(clk->type == SUNXI_CCU_PHASE); + + clkp = &clk->base; + + p_rate = sunxi_ccu_phase_get_parent_rate(clkp); + if (p_rate == 0) + return 0; + gp_rate = sunxi_ccu_phase_get_parent_rate(clk_get_parent(clkp)); + if (gp_rate == 0) + return 0; + + p_div = gp_rate / p_rate; + + delay = new_rate == 180 ? 0 : + sunxi_ccu_phase_div(new_rate, + sunxi_ccu_phase_div(360, p_div)); + + val = CCU_READ(sc, phase->reg); + val &= ~phase->mask; + val |= __SHIFTIN(delay, phase->mask); + CCU_WRITE(sc, phase->reg, val); + + return 0; +} + +const char * +sunxi_ccu_phase_get_parent(struct sunxi_ccu_softc *sc, + struct sunxi_ccu_clk *clk) +{ + struct sunxi_ccu_phase *phase = &clk->u.phase; + + KASSERT(clk->type == SUNXI_CCU_PHASE); + + return phase->parent; +}