/* $OpenBSD: imxanatop.c,v 1.5 2018/08/30 12:14:30 jsg Exp $ */ /* * Copyright (c) 2016 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #define ANALOG_PLL_ARM 0x0000 #define ANALOG_PLL_ARM_SET 0x0004 #define ANALOG_PLL_ARM_CLR 0x0008 #define ANALOG_PLL_ARM_DIV_SELECT_MASK 0x7f #define ANALOG_PLL_ARM_BYPASS (1 << 16) #define ANALOG_PLL_USB1 0x0010 #define ANALOG_PLL_USB1_SET 0x0014 #define ANALOG_PLL_USB1_CLR 0x0018 #define ANALOG_PLL_USB1_DIV_SELECT_MASK 0x1 #define ANALOG_PLL_USB1_EN_USB_CLKS (1 << 6) #define ANALOG_PLL_USB1_POWER (1 << 12) #define ANALOG_PLL_USB1_ENABLE (1 << 13) #define ANALOG_PLL_USB1_BYPASS (1 << 16) #define ANALOG_PLL_USB1_LOCK (1U << 31) #define ANALOG_PLL_USB2 0x0020 #define ANALOG_PLL_USB2_SET 0x0024 #define ANALOG_PLL_USB2_CLR 0x0028 #define ANALOG_PLL_USB2_DIV_SELECT_MASK 0x1 #define ANALOG_PLL_USB2_EN_USB_CLKS (1 << 6) #define ANALOG_PLL_USB2_POWER (1 << 12) #define ANALOG_PLL_USB2_ENABLE (1 << 13) #define ANALOG_PLL_USB2_BYPASS (1 << 16) #define ANALOG_PLL_USB2_LOCK (1U << 31) #define ANALOG_PLL_SYS 0x0030 #define ANALOG_PLL_SYS_DIV_SELECT_MASK 0x1 #define ANALOG_PLL_ENET 0x00e0 #define ANALOG_PLL_ENET_SET 0x00e4 #define ANALOG_PLL_ENET_CLR 0x00e8 #define ANALOG_PLL_ENET_DIV_125M (1 << 11) #define ANALOG_PLL_ENET_POWERDOWN (1 << 12) #define ANALOG_PLL_ENET_ENABLE (1 << 13) #define ANALOG_PLL_ENET_BYPASS (1 << 16) #define ANALOG_PLL_ENET_125M_PCIE (1 << 19) #define ANALOG_PLL_ENET_100M_SATA (1 << 20) #define ANALOG_PLL_ENET_LOCK (1U << 31) #define ANALOG_PFD_480 0x00f0 #define ANALOG_PFD_480_SET 0x00f4 #define ANALOG_PFD_480_CLR 0x00f8 #define ANALOG_PFD_480_PFDx_FRAC(x, y) (((x) >> ((y) << 3)) & 0x3f) #define ANALOG_PFD_528 0x0100 #define ANALOG_PFD_528_SET 0x0104 #define ANALOG_PFD_528_CLR 0x0108 #define ANALOG_PFD_528_PFDx_FRAC(x, y) (((x) >> ((y) << 3)) & 0x3f) #define ANALOG_USB1_CHRG_DETECT 0x01b0 #define ANALOG_USB1_CHRG_DETECT_SET 0x01b4 #define ANALOG_USB1_CHRG_DETECT_CLR 0x01b8 #define ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B (1 << 19) #define ANALOG_USB1_CHRG_DETECT_EN_B (1 << 20) #define ANALOG_USB2_CHRG_DETECT 0x0210 #define ANALOG_USB2_CHRG_DETECT_SET 0x0214 #define ANALOG_USB2_CHRG_DETECT_CLR 0x0218 #define ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B (1 << 19) #define ANALOG_USB2_CHRG_DETECT_EN_B (1 << 20) #define ANALOG_DIGPROG 0x0260 #define ANALOG_DIGPROG_MINOR_MASK 0xff #define HCLK_FREQ 24000000 #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) #define HSET4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) #define HCLR4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) struct imxanatop_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; }; struct imxanatop_softc *imxanatop_sc; struct imxanatop_regulator { struct imxanatop_softc *ir_sc; uint32_t ir_reg_offset; uint32_t ir_vol_bit_shift; uint32_t ir_vol_bit_width; uint32_t ir_min_bit_val; uint32_t ir_min_voltage; uint32_t ir_max_voltage; uint32_t ir_delay_reg_offset; uint32_t ir_delay_bit_shift; uint32_t ir_delay_bit_width; struct regulator_device ir_rd; }; int imxanatop_match(struct device *, void *, void *); void imxanatop_attach(struct device *, struct device *, void *); struct cfattach imxanatop_ca = { sizeof(struct imxanatop_softc), imxanatop_match, imxanatop_attach }; struct cfdriver imxanatop_cd = { NULL, "imxanatop", DV_DULL }; void imxanatop_attach_regulator(struct imxanatop_softc *, int); uint32_t imxanatop_get_voltage(void *); int imxanatop_set_voltage(void *, uint32_t); int imxanatop_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; if (OF_is_compatible(faa->fa_node, "fsl,imx6q-anatop")) return 10; /* Must beat simplebus(4) and syscon(4). */ return 0; } void imxanatop_attach(struct device *parent, struct device *self, void *aux) { struct imxanatop_softc *sc = (struct imxanatop_softc *)self; struct fdt_attach_args *faa = aux; int node; if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } sc->sc_iot = faa->fa_iot; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) { printf(": can't map registers\n"); return; } regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); printf("\n"); for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) if (OF_is_compatible(node, "fsl,anatop-regulator")) imxanatop_attach_regulator(sc, node); imxanatop_sc = sc; } void imxanatop_attach_regulator(struct imxanatop_softc *sc, int node) { struct imxanatop_regulator *ir; ir = malloc(sizeof(*ir), M_DEVBUF, M_WAITOK | M_ZERO); ir->ir_sc = sc; ir->ir_reg_offset = OF_getpropint(node, "anatop-reg-offset", -1); ir->ir_vol_bit_shift = OF_getpropint(node, "anatop-vol-bit-shift", -1); ir->ir_vol_bit_width = OF_getpropint(node, "anatop-vol-bit-width", -1); ir->ir_min_bit_val = OF_getpropint(node, "anatop-min-bit-val", -1); ir->ir_min_voltage = OF_getpropint(node, "anatop-min-voltage", -1); ir->ir_max_voltage = OF_getpropint(node, "anatop-max-voltage", -1); if (ir->ir_reg_offset == -1 || ir->ir_vol_bit_shift == -1 || ir->ir_vol_bit_width == -1 || ir->ir_min_bit_val == -1 || ir->ir_min_voltage == -1 || ir->ir_max_voltage == -1) { free(ir, M_DEVBUF, sizeof(*ir)); return; } ir->ir_delay_reg_offset = OF_getpropint(node, "anatop-delay-reg-offset", 0); ir->ir_delay_bit_shift = OF_getpropint(node, "anatop-delay-bit-shift", 0); ir->ir_delay_bit_width = OF_getpropint(node, "anatop-delay-bit-width", 0); ir->ir_rd.rd_node = node; ir->ir_rd.rd_cookie = ir; ir->ir_rd.rd_get_voltage = imxanatop_get_voltage; ir->ir_rd.rd_set_voltage = imxanatop_set_voltage; regulator_register(&ir->ir_rd); } uint32_t imxanatop_get_voltage(void *cookie) { struct imxanatop_regulator *ir = cookie; uint32_t bit_val; bit_val = HREAD4(ir->ir_sc, ir->ir_reg_offset) >> ir->ir_vol_bit_shift; bit_val &= ((1 << ir->ir_vol_bit_width) - 1); return (ir->ir_min_voltage + (bit_val - ir->ir_min_bit_val) * 25000); } int imxanatop_set_voltage(void *cookie, uint32_t voltage) { struct imxanatop_regulator *ir = cookie; uint32_t bit_val, old_bit_val, reg; int steps, usecs; if (voltage < ir->ir_min_voltage || voltage > ir->ir_max_voltage) return -1; bit_val = ir->ir_min_bit_val + (voltage - ir->ir_min_voltage) / 25000; reg = HREAD4(ir->ir_sc, ir->ir_reg_offset); old_bit_val = (reg >> ir->ir_vol_bit_shift); old_bit_val &= ((1 << ir->ir_vol_bit_width) -1); reg &= ~((1 << ir->ir_vol_bit_width) - 1) << ir->ir_vol_bit_shift; reg |= (bit_val << ir->ir_vol_bit_shift); HWRITE4(ir->ir_sc, ir->ir_reg_offset, reg); steps = bit_val - old_bit_val; if (steps > 0 && ir->ir_delay_bit_width > 0) { reg = HREAD4(ir->ir_sc, ir->ir_delay_reg_offset); reg >>= ir->ir_delay_bit_shift; reg &= ((1 << ir->ir_delay_bit_width) - 1); usecs = ((reg + 1) * steps * 64 * 1000000) / 24000000; delay(usecs); } return 0; } uint32_t imxanatop_decode_pll(enum imxanatop_clocks pll, uint32_t freq) { struct imxanatop_softc *sc = imxanatop_sc; uint32_t div; KASSERT(sc != NULL); switch (pll) { case ARM_PLL1: if (HREAD4(sc, ANALOG_PLL_ARM) & ANALOG_PLL_ARM_BYPASS) return freq; div = HREAD4(sc, ANALOG_PLL_ARM) & ANALOG_PLL_ARM_DIV_SELECT_MASK; return (freq * div) / 2; case SYS_PLL2: div = HREAD4(sc, ANALOG_PLL_SYS) & ANALOG_PLL_SYS_DIV_SELECT_MASK; return freq * (20 + (div << 1)); case USB1_PLL3: div = HREAD4(sc, ANALOG_PLL_USB2) & ANALOG_PLL_USB2_DIV_SELECT_MASK; return freq * (20 + (div << 1)); default: return 0; } } uint32_t imxanatop_get_pll2_pfd(unsigned int pfd) { struct imxanatop_softc *sc = imxanatop_sc; KASSERT(sc != NULL); return imxanatop_decode_pll(SYS_PLL2, HCLK_FREQ) * 18ULL / ANALOG_PFD_528_PFDx_FRAC(HREAD4(sc, ANALOG_PFD_528), pfd); } uint32_t imxanatop_get_pll3_pfd(unsigned int pfd) { struct imxanatop_softc *sc = imxanatop_sc; KASSERT(sc != NULL); return imxanatop_decode_pll(USB1_PLL3, HCLK_FREQ) * 18ULL / ANALOG_PFD_480_PFDx_FRAC(HREAD4(sc, ANALOG_PFD_480), pfd); }