Index: head/sys/arm/ti/ti_hwmods.c =================================================================== --- head/sys/arm/ti/ti_hwmods.c (revision 363699) +++ head/sys/arm/ti/ti_hwmods.c (nonexistent) @@ -1,214 +0,0 @@ -/*- - * Copyright (c) 2015 Oleksandr Tymoshenko - * 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. - * - * $FreeBSD$ - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -struct hwmod { - const char *name; - int clock_id; -}; - -struct hwmod ti_hwmods[] = { - {"i2c1", I2C1_CLK}, - {"i2c2", I2C2_CLK}, - {"i2c3", I2C3_CLK}, - {"i2c4", I2C4_CLK}, - {"i2c5", I2C5_CLK}, - - {"gpio1", GPIO1_CLK}, - {"gpio2", GPIO2_CLK}, - {"gpio3", GPIO3_CLK}, - {"gpio4", GPIO4_CLK}, - {"gpio5", GPIO5_CLK}, - {"gpio6", GPIO6_CLK}, - {"gpio7", GPIO7_CLK}, - - {"mmc1", MMC1_CLK}, - {"mmc2", MMC2_CLK}, - {"mmc3", MMC3_CLK}, - {"mmc4", MMC4_CLK}, - {"mmc5", MMC5_CLK}, - {"mmc6", MMC6_CLK}, - - {"epwmss0", PWMSS0_CLK}, - {"epwmss1", PWMSS1_CLK}, - {"epwmss2", PWMSS2_CLK}, - - {"spi0", SPI0_CLK}, - {"spi1", SPI1_CLK}, - - {"timer1", TIMER1_CLK}, - {"timer2", TIMER2_CLK}, - {"timer3", TIMER3_CLK}, - {"timer4", TIMER4_CLK}, - {"timer5", TIMER5_CLK}, - {"timer6", TIMER6_CLK}, - {"timer7", TIMER7_CLK}, - - {"uart1", UART1_CLK}, - {"uart2", UART2_CLK}, - {"uart3", UART3_CLK}, - {"uart4", UART4_CLK}, - {"uart5", UART5_CLK}, - {"uart6", UART6_CLK}, - {"uart7", UART7_CLK}, - - {NULL, 0} -}; - -static inline int -ti_get_hwmods_prop(phandle_t node, void **name) -{ - int len; - - if ((len = OF_getprop_alloc(node, "ti,hwmods", name)) > 0) - return (len); - return (OF_getprop_alloc(OF_parent(node), "ti,hwmods", name)); -} - -clk_ident_t -ti_hwmods_get_clock(device_t dev) -{ - phandle_t node; - int len, l; - char *name; - char *buf; - int clk; - struct hwmod *hw; - - if ((node = ofw_bus_get_node(dev)) == 0) - return (INVALID_CLK_IDENT); - - if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0) - return (INVALID_CLK_IDENT); - - buf = name; - - clk = INVALID_CLK_IDENT; - while ((len > 0) && (clk == INVALID_CLK_IDENT)) { - for (hw = ti_hwmods; hw->name != NULL; ++hw) { - if (strcmp(hw->name, name) == 0) { - clk = hw->clock_id; - break; - } - } - - /* Slide to the next sub-string. */ - l = strlen(name) + 1; - name += l; - len -= l; - } - - if (len > 0) - device_printf(dev, "WARNING: more than one ti,hwmod \n"); - - OF_prop_free(buf); - return (clk); -} - -int ti_hwmods_contains(device_t dev, const char *hwmod) -{ - phandle_t node; - int len, l; - char *name; - char *buf; - int result; - - if ((node = ofw_bus_get_node(dev)) == 0) - return (0); - - if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0) - return (0); - - buf = name; - - result = 0; - while (len > 0) { - if (strcmp(name, hwmod) == 0) { - result = 1; - break; - } - - /* Slide to the next sub-string. */ - l = strlen(name) + 1; - name += l; - len -= l; - } - - OF_prop_free(buf); - - return (result); -} - -int -ti_hwmods_get_unit(device_t dev, const char *hwmod) -{ - phandle_t node; - int l, len, hwmodlen, result; - char *name; - char *buf; - - if ((node = ofw_bus_get_node(dev)) == 0) - return (0); - - if ((len = ti_get_hwmods_prop(node, (void **)&name)) <= 0) - return (0); - - buf = name; - hwmodlen = strlen(hwmod); - result = 0; - while (len > 0) { - if (strncmp(name, hwmod, hwmodlen) == 0) { - result = (int)strtoul(name + hwmodlen, NULL, 10); - break; - } - /* Slide to the next sub-string. */ - l = strlen(name) + 1; - name += l; - len -= l; - } - - OF_prop_free(buf); - return (result); -} Property changes on: head/sys/arm/ti/ti_hwmods.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_hwmods.h =================================================================== --- head/sys/arm/ti/ti_hwmods.h (revision 363699) +++ head/sys/arm/ti/ti_hwmods.h (nonexistent) @@ -1,37 +0,0 @@ -/*- - * Copyright (c) 2015 Oleksandr Tymoshenko - * 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. - * - * $FreeBSD$ - */ -#ifndef _TI_HWMODS_H_ -#define _TI_HWMODS_H_ - -clk_ident_t ti_hwmods_get_clock(device_t dev); -int ti_hwmods_contains(device_t dev, const char *hwmod); - -/* Returns the N from "hwmodN" in the ti,hwmods property; 0 on failure. */ -int ti_hwmods_get_unit(device_t dev, const char *hwmod); - -#endif /* _TI_HWMODS_H_ */ Property changes on: head/sys/arm/ti/ti_hwmods.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/ti/am335x/am335x_prcm.c =================================================================== --- head/sys/arm/ti/am335x/am335x_prcm.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_prcm.c (nonexistent) @@ -1,884 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2012 Damjan Marion - * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include "am335x_scm.h" - -#define CM_PER 0 -#define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000) -#define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004) -#define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C) -#define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014) -#define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018) -#define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C) -#define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024) -#define CM_PER_UART5_CLKCTRL (CM_PER + 0x038) -#define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C) -#define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044) -#define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048) -#define CM_PER_SPI0_CLKCTRL (CM_PER + 0x04C) -#define CM_PER_SPI1_CLKCTRL (CM_PER + 0x050) -#define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C) -#define CM_PER_UART2_CLKCTRL (CM_PER + 0x070) -#define CM_PER_UART3_CLKCTRL (CM_PER + 0x074) -#define CM_PER_UART4_CLKCTRL (CM_PER + 0x078) -#define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C) -#define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080) -#define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084) -#define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088) -#define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC) -#define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0) -#define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4) -#define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC) -#define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC) -#define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4) -#define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8) -#define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC) -#define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0) -#define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8) -#define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC) -#define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0) -#define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4) -#define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8) -#define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC) -#define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100) -#define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C) -#define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110) -#define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C) -#define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130) -#define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144) -#define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140) - -#define CM_WKUP 0x400 -#define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000) -#define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004) -#define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008) -#define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C) -#define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C) -#define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048) -#define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054) -#define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C) -#define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098) -#define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8) -#define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC) - -#define CM_DPLL 0x500 -#define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004) -#define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008) -#define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C) -#define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010) -#define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018) -#define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C) -#define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030) - -#define CM_RTC 0x800 -#define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000) -#define CM_RTC_CLKSTCTRL (CM_RTC + 0x004) - -#define PRM_PER 0xC00 -#define PRM_PER_RSTCTRL (PRM_PER + 0x00) - -#define PRM_DEVICE_OFFSET 0xF00 -#define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00) - -struct am335x_prcm_softc { - struct resource * res[2]; - bus_space_tag_t bst; - bus_space_handle_t bsh; - int attach_done; -}; - -static struct resource_spec am335x_prcm_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { -1, 0 } -}; - -static struct am335x_prcm_softc *am335x_prcm_sc = NULL; - -static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev); -static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev); -static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); -static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); -static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); -static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); -static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); -static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq); -static int am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq); -static void am335x_prcm_reset(void); -static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev); -static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev); - -#define AM335X_NOOP_CLOCK_DEV(i) \ - { .id = (i), \ - .clk_activate = am335x_clk_noop_activate, \ - .clk_deactivate = am335x_clk_noop_deactivate, \ - .clk_set_source = am335x_clk_noop_set_source, \ - .clk_accessible = NULL, \ - .clk_get_source_freq = NULL, \ - .clk_set_source_freq = NULL \ - } - -#define AM335X_GENERIC_CLOCK_DEV(i) \ - { .id = (i), \ - .clk_activate = am335x_clk_generic_activate, \ - .clk_deactivate = am335x_clk_generic_deactivate, \ - .clk_set_source = am335x_clk_generic_set_source, \ - .clk_accessible = NULL, \ - .clk_get_source_freq = NULL, \ - .clk_set_source_freq = NULL \ - } - -#define AM335X_GPIO_CLOCK_DEV(i) \ - { .id = (i), \ - .clk_activate = am335x_clk_gpio_activate, \ - .clk_deactivate = am335x_clk_generic_deactivate, \ - .clk_set_source = am335x_clk_generic_set_source, \ - .clk_accessible = NULL, \ - .clk_get_source_freq = NULL, \ - .clk_set_source_freq = NULL \ - } - -#define AM335X_MMCHS_CLOCK_DEV(i) \ - { .id = (i), \ - .clk_activate = am335x_clk_generic_activate, \ - .clk_deactivate = am335x_clk_generic_deactivate, \ - .clk_set_source = am335x_clk_generic_set_source, \ - .clk_accessible = NULL, \ - .clk_get_source_freq = am335x_clk_hsmmc_get_source_freq, \ - .clk_set_source_freq = NULL \ - } - -struct ti_clock_dev ti_am335x_clk_devmap[] = { - /* System clocks */ - { .id = SYS_CLK, - .clk_activate = NULL, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = am335x_clk_get_sysclk_freq, - .clk_set_source_freq = NULL, - }, - /* MPU (ARM) core clocks */ - { .id = MPU_CLK, - .clk_activate = NULL, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = am335x_clk_get_arm_fclk_freq, - .clk_set_source_freq = NULL, - }, - /* CPSW Ethernet Switch core clocks */ - { .id = CPSW_CLK, - .clk_activate = am335x_clk_cpsw_activate, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = NULL, - .clk_set_source_freq = NULL, - }, - - /* Mentor USB HS controller core clocks */ - { .id = MUSB0_CLK, - .clk_activate = am335x_clk_musb0_activate, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = NULL, - .clk_set_source_freq = NULL, - }, - - /* LCD controller clocks */ - { .id = LCDC_CLK, - .clk_activate = am335x_clk_lcdc_activate, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = am335x_clk_get_arm_disp_freq, - .clk_set_source_freq = am335x_clk_set_arm_disp_freq, - }, - - /* UART */ - AM335X_NOOP_CLOCK_DEV(UART1_CLK), - AM335X_GENERIC_CLOCK_DEV(UART2_CLK), - AM335X_GENERIC_CLOCK_DEV(UART3_CLK), - AM335X_GENERIC_CLOCK_DEV(UART4_CLK), - AM335X_GENERIC_CLOCK_DEV(UART5_CLK), - AM335X_GENERIC_CLOCK_DEV(UART6_CLK), - - /* DMTimer */ - AM335X_GENERIC_CLOCK_DEV(TIMER2_CLK), - AM335X_GENERIC_CLOCK_DEV(TIMER3_CLK), - AM335X_GENERIC_CLOCK_DEV(TIMER4_CLK), - AM335X_GENERIC_CLOCK_DEV(TIMER5_CLK), - AM335X_GENERIC_CLOCK_DEV(TIMER6_CLK), - AM335X_GENERIC_CLOCK_DEV(TIMER7_CLK), - - /* GPIO, we use hwmods as reference, not units in spec */ - AM335X_GPIO_CLOCK_DEV(GPIO1_CLK), - AM335X_GPIO_CLOCK_DEV(GPIO2_CLK), - AM335X_GPIO_CLOCK_DEV(GPIO3_CLK), - AM335X_GPIO_CLOCK_DEV(GPIO4_CLK), - - /* I2C we use hwmods as reference, not units in spec */ - AM335X_GENERIC_CLOCK_DEV(I2C1_CLK), - AM335X_GENERIC_CLOCK_DEV(I2C2_CLK), - AM335X_GENERIC_CLOCK_DEV(I2C3_CLK), - - /* McSPI we use hwmods as reference, not units in spec */ - AM335X_GENERIC_CLOCK_DEV(SPI0_CLK), - AM335X_GENERIC_CLOCK_DEV(SPI1_CLK), - - /* TSC_ADC */ - AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK), - - /* EDMA */ - AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK), - AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK), - AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK), - AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK), - - /* MMCHS */ - AM335X_MMCHS_CLOCK_DEV(MMC1_CLK), - AM335X_MMCHS_CLOCK_DEV(MMC2_CLK), - AM335X_MMCHS_CLOCK_DEV(MMC3_CLK), - - /* PWMSS */ - AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK), - AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK), - AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK), - - /* System Mailbox clock */ - AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK), - - /* SPINLOCK */ - AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK), - - /* PRU-ICSS */ - { .id = PRUSS_CLK, - .clk_activate = am335x_clk_pruss_activate, - .clk_deactivate = NULL, - .clk_set_source = NULL, - .clk_accessible = NULL, - .clk_get_source_freq = NULL, - .clk_set_source_freq = NULL, - }, - - /* RTC */ - AM335X_GENERIC_CLOCK_DEV(RTC_CLK), - - { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } -}; - -struct am335x_clk_details { - clk_ident_t id; - uint32_t clkctrl_reg; - uint32_t clksel_reg; -}; - -#define _CLK_DETAIL(i, c, s) \ - { .id = (i), \ - .clkctrl_reg = (c), \ - .clksel_reg = (s), \ - } - -static struct am335x_clk_details g_am335x_clk_details[] = { - - /* UART. UART0 clock not controllable. */ - _CLK_DETAIL(UART1_CLK, 0, 0), - _CLK_DETAIL(UART2_CLK, CM_PER_UART1_CLKCTRL, 0), - _CLK_DETAIL(UART3_CLK, CM_PER_UART2_CLKCTRL, 0), - _CLK_DETAIL(UART4_CLK, CM_PER_UART3_CLKCTRL, 0), - _CLK_DETAIL(UART5_CLK, CM_PER_UART4_CLKCTRL, 0), - _CLK_DETAIL(UART6_CLK, CM_PER_UART5_CLKCTRL, 0), - - /* DMTimer modules */ - _CLK_DETAIL(TIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK), - _CLK_DETAIL(TIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK), - _CLK_DETAIL(TIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK), - _CLK_DETAIL(TIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK), - _CLK_DETAIL(TIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK), - _CLK_DETAIL(TIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK), - - /* GPIO modules, hwmods start with gpio1 */ - _CLK_DETAIL(GPIO1_CLK, CM_WKUP_GPIO0_CLKCTRL, 0), - _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO1_CLKCTRL, 0), - _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO2_CLKCTRL, 0), - _CLK_DETAIL(GPIO4_CLK, CM_PER_GPIO3_CLKCTRL, 0), - - /* I2C modules, hwmods start with i2c1 */ - _CLK_DETAIL(I2C1_CLK, CM_WKUP_I2C0_CLKCTRL, 0), - _CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0), - _CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0), - - /* McSPI modules, hwmods start with spi0 */ - _CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0), - _CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0), - - /* TSC_ADC module */ - _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0), - - /* EDMA modules */ - _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0), - _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0), - _CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0), - _CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0), - - /* MMCHS modules, hwmods start with mmc1*/ - _CLK_DETAIL(MMC1_CLK, CM_PER_MMC0_CLKCTRL, 0), - _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0), - _CLK_DETAIL(MMC3_CLK, CM_PER_MMC1_CLKCTRL, 0), - - /* PWMSS modules */ - _CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0), - _CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0), - _CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0), - - _CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0), - _CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0), - - /* RTC module */ - _CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0), - - { INVALID_CLK_IDENT, 0}, -}; - -/* Read/Write macros */ -#define prcm_read_4(reg) \ - bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg) -#define prcm_write_4(reg, val) \ - bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val) - -void am335x_prcm_setup_dmtimer(int); - -static int -am335x_prcm_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (ofw_bus_is_compatible(dev, "ti,am3-prcm")) { - device_set_desc(dev, "AM335x Power and Clock Management"); - return(BUS_PROBE_DEFAULT); - } - - return (ENXIO); -} - -static int -am335x_prcm_attach(device_t dev) -{ - struct am335x_prcm_softc *sc = device_get_softc(dev); - - if (am335x_prcm_sc) - return (ENXIO); - - if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) { - device_printf(dev, "could not allocate resources\n"); - return (ENXIO); - } - - sc->bst = rman_get_bustag(sc->res[0]); - sc->bsh = rman_get_bushandle(sc->res[0]); - - am335x_prcm_sc = sc; - ti_cpu_reset = am335x_prcm_reset; - - return (0); -} - -static void -am335x_prcm_new_pass(device_t dev) -{ - struct am335x_prcm_softc *sc = device_get_softc(dev); - unsigned int sysclk, fclk; - - sc = device_get_softc(dev); - if (sc->attach_done || - bus_current_pass < (BUS_PASS_TIMER + BUS_PASS_ORDER_EARLY)) { - bus_generic_new_pass(dev); - return; - } - - sc->attach_done = 1; - - if (am335x_clk_get_sysclk_freq(NULL, &sysclk) != 0) - sysclk = 0; - if (am335x_clk_get_arm_fclk_freq(NULL, &fclk) != 0) - fclk = 0; - if (sysclk && fclk) - device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n", - sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000); - else { - device_printf(dev, "can't read frequencies yet (SCM device not ready?)\n"); - goto fail; - } - - return; - -fail: - device_detach(dev); - return; -} - -static device_method_t am335x_prcm_methods[] = { - DEVMETHOD(device_probe, am335x_prcm_probe), - DEVMETHOD(device_attach, am335x_prcm_attach), - - /* Bus interface */ - DEVMETHOD(bus_new_pass, am335x_prcm_new_pass), - { 0, 0 } -}; - -static driver_t am335x_prcm_driver = { - "am335x_prcm", - am335x_prcm_methods, - sizeof(struct am335x_prcm_softc), -}; - -static devclass_t am335x_prcm_devclass; - -EARLY_DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver, - am335x_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); -MODULE_VERSION(am335x_prcm, 1); -MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1); - -static struct am335x_clk_details* -am335x_clk_details(clk_ident_t id) -{ - struct am335x_clk_details *walker; - - for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { - if (id == walker->id) - return (walker); - } - - return NULL; -} - -static int -am335x_clk_noop_activate(struct ti_clock_dev *clkdev) -{ - - return (0); -} - -static int -am335x_clk_generic_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - struct am335x_clk_details* clk_details; - - if (sc == NULL) - return ENXIO; - - clk_details = am335x_clk_details(clkdev->id); - - if (clk_details == NULL) - return (ENXIO); - - /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ - prcm_write_4(clk_details->clkctrl_reg, 2); - while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2) - DELAY(10); - - return (0); -} - -static int -am335x_clk_gpio_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - struct am335x_clk_details* clk_details; - - if (sc == NULL) - return ENXIO; - - clk_details = am335x_clk_details(clkdev->id); - - if (clk_details == NULL) - return (ENXIO); - - /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ - /* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */ - prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18)); - while ((prcm_read_4(clk_details->clkctrl_reg) & - (3 | (1 << 18) )) != (2 | (1 << 18))) - DELAY(10); - - return (0); -} - -static int -am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev) -{ - - return(0); -} - -static int -am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - struct am335x_clk_details* clk_details; - - if (sc == NULL) - return ENXIO; - - clk_details = am335x_clk_details(clkdev->id); - - if (clk_details == NULL) - return (ENXIO); - - /* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */ - prcm_write_4(clk_details->clkctrl_reg, 0); - while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0) - DELAY(10); - - return (0); -} - -static int -am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) -{ - - return (0); -} - -static int -am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - struct am335x_clk_details* clk_details; - uint32_t reg; - - if (sc == NULL) - return ENXIO; - - clk_details = am335x_clk_details(clkdev->id); - - if (clk_details == NULL) - return (ENXIO); - - switch (clksrc) { - case EXT_CLK: - reg = 0; /* SEL2: TCLKIN clock */ - break; - case SYSCLK_CLK: - reg = 1; /* SEL1: CLK_M_OSC clock */ - break; - case F32KHZ_CLK: - reg = 2; /* SEL3: CLK_32KHZ clock */ - break; - default: - return (ENXIO); - } - - prcm_write_4(clk_details->clksel_reg, reg); - while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg) - DELAY(10); - - return (0); -} - -static int -am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq) -{ - *freq = 96000000; - return (0); -} - -static int -am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) -{ - uint32_t ctrl_status; - - /* Read the input clock freq from the control module. */ - if (ti_scm_reg_read_4(SCM_CTRL_STATUS, &ctrl_status)) - return (ENXIO); - - switch ((ctrl_status>>22) & 0x3) { - case 0x0: - /* 19.2Mhz */ - *freq = 19200000; - break; - case 0x1: - /* 24Mhz */ - *freq = 24000000; - break; - case 0x2: - /* 25Mhz */ - *freq = 25000000; - break; - case 0x3: - /* 26Mhz */ - *freq = 26000000; - break; - } - - return (0); -} - -#define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) -#define DPLL_DIV(reg) ((reg & 0x7f)+1) -#define DPLL_MULT(reg) ((reg>>8) & 0x7FF) -#define DPLL_MAX_MUL 0x800 -#define DPLL_MAX_DIV 0x80 - -static int -am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) -{ - uint32_t reg; - uint32_t sysclk; - - reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU); - - /*Check if we are running in bypass */ - if (DPLL_BYP_CLKSEL(reg)) - return ENXIO; - - am335x_clk_get_sysclk_freq(NULL, &sysclk); - *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); - return(0); -} - -static int -am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq) -{ - uint32_t reg; - uint32_t sysclk; - - reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP); - - /*Check if we are running in bypass */ - if (DPLL_BYP_CLKSEL(reg)) - return ENXIO; - - am335x_clk_get_sysclk_freq(NULL, &sysclk); - *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); - return(0); -} - -static int -am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq) -{ - uint32_t sysclk; - uint32_t mul, div; - uint32_t i, j; - unsigned int delta, min_delta; - - am335x_clk_get_sysclk_freq(NULL, &sysclk); - - /* Bypass mode */ - prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4); - - /* Make sure it's in bypass mode */ - while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) - & (1 << 8))) - DELAY(10); - - /* Dumb and non-optimal implementation */ - min_delta = freq; - for (i = 1; i < DPLL_MAX_MUL; i++) { - for (j = 1; j < DPLL_MAX_DIV; j++) { - delta = abs(freq - i*(sysclk/j)); - if (delta < min_delta) { - mul = i; - div = j; - min_delta = delta; - } - if (min_delta == 0) - break; - } - } - - prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (mul << 8) | (div - 1)); - - /* Locked mode */ - prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7); - - int timeout = 10000; - while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) - & (1 << 0))) && timeout--) - DELAY(10); - - return(0); -} - -static void -am335x_prcm_reset(void) -{ - prcm_write_4(PRM_RSTCTRL, (1<<1)); -} - -static int -am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - - if (sc == NULL) - return ENXIO; - - /* set MODULENAME to ENABLE */ - prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2); - - /* wait for IDLEST to become Func(0) */ - while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16)); - - /*set CLKTRCTRL to SW_WKUP(2) */ - prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2); - - /* wait for 125 MHz OCP clock to become active */ - while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0); - return(0); -} - -static int -am335x_clk_musb0_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - - if (sc == NULL) - return ENXIO; - - /* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */ - /* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/ - prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300); - - /*set MODULEMODE to ENABLE(2) */ - prcm_write_4(CM_PER_USB0_CLKCTRL, 2); - - /* wait for MODULEMODE to become ENABLE(2) */ - while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2) - DELAY(10); - - /* wait for IDLEST to become Func(0) */ - while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16)) - DELAY(10); - - return(0); -} - -static int -am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - - if (sc == NULL) - return (ENXIO); - - /* - * For now set frequency to 2*VGA_PIXEL_CLOCK - */ - am335x_clk_set_arm_disp_freq(clkdev, 25175000*2); - - /*set MODULEMODE to ENABLE(2) */ - prcm_write_4(CM_PER_LCDC_CLKCTRL, 2); - - /* wait for MODULEMODE to become ENABLE(2) */ - while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2) - DELAY(10); - - /* wait for IDLEST to become Func(0) */ - while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16)) - DELAY(10); - - return (0); -} - -static int -am335x_clk_pruss_activate(struct ti_clock_dev *clkdev) -{ - struct am335x_prcm_softc *sc = am335x_prcm_sc; - - if (sc == NULL) - return (ENXIO); - - /* Set MODULEMODE to ENABLE(2) */ - prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2); - - /* Wait for MODULEMODE to become ENABLE(2) */ - while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2) - DELAY(10); - - /* Set CLKTRCTRL to SW_WKUP(2) */ - prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2); - - /* Wait for the 200 MHz OCP clock to become active */ - while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0) - DELAY(10); - - /* Wait for the 200 MHz IEP clock to become active */ - while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0) - DELAY(10); - - /* Wait for the 192 MHz UART clock to become active */ - while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0) - DELAY(10); - - /* Select L3F as OCP clock */ - prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0); - while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0) - DELAY(10); - - /* Clear the RESET bit */ - prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2); - - return (0); -} Property changes on: head/sys/arm/ti/am335x/am335x_prcm.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/ti/am335x/am335x_usbss.c =================================================================== --- head/sys/arm/ti/am335x/am335x_usbss.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_usbss.c (nonexistent) @@ -1,226 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2013 Oleksandr Tymoshenko - * - * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define AM335X_USB_PORTS 2 - -#define USBSS_REVREG 0x00 -#define USBSS_SYSCONFIG 0x10 -#define USBSS_SYSCONFIG_SRESET 1 - -#define USBCTRL_REV 0x00 -#define USBCTRL_CTRL 0x14 -#define USBCTRL_STAT 0x18 -#define USBCTRL_IRQ_STAT0 0x30 -#define IRQ_STAT0_RXSHIFT 16 -#define IRQ_STAT0_TXSHIFT 0 -#define USBCTRL_IRQ_STAT1 0x34 -#define IRQ_STAT1_DRVVBUS (1 << 8) -#define USBCTRL_INTEN_SET0 0x38 -#define USBCTRL_INTEN_SET1 0x3C -#define USBCTRL_INTEN_USB_ALL 0x1ff -#define USBCTRL_INTEN_USB_SOF (1 << 3) -#define USBCTRL_INTEN_CLR0 0x40 -#define USBCTRL_INTEN_CLR1 0x44 -#define USBCTRL_UTMI 0xE0 -#define USBCTRL_UTMI_FSDATAEXT (1 << 1) -#define USBCTRL_MODE 0xE8 -#define USBCTRL_MODE_IDDIG (1 << 8) -#define USBCTRL_MODE_IDDIGMUX (1 << 7) - -#define USBSS_WRITE4(sc, reg, val) \ - bus_write_4((sc)->sc_mem_res, (reg), (val)) -#define USBSS_READ4(sc, reg) \ - bus_read_4((sc)->sc_mem_res, (reg)) - -static device_probe_t usbss_probe; -static device_attach_t usbss_attach; -static device_detach_t usbss_detach; - -struct usbss_softc { - struct simplebus_softc simplebus_sc; - struct resource *sc_mem_res; - int sc_mem_rid; -}; - -static int -usbss_probe(device_t dev) -{ - - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - - if (!ofw_bus_is_compatible(dev, "ti,am33xx-usb")) - return (ENXIO); - - device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); - - return (BUS_PROBE_DEFAULT); -} - -static int -usbss_attach(device_t dev) -{ - struct usbss_softc *sc = device_get_softc(dev); - int i; - uint32_t rev; - phandle_t node; - - /* Request the memory resources */ - sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->sc_mem_rid, RF_ACTIVE); - if (sc->sc_mem_res == NULL) { - device_printf(dev, - "Error: could not allocate mem resources\n"); - return (ENXIO); - } - - /* Enable device clocks. */ - ti_prcm_clk_enable(MUSB0_CLK); - - /* - * Reset USBSS, USB0 and USB1. - * The registers of USB subsystem must not be accessed while the - * reset pulse is active (200ns). - */ - USBSS_WRITE4(sc, USBSS_SYSCONFIG, USBSS_SYSCONFIG_SRESET); - DELAY(100); - i = 10; - while (USBSS_READ4(sc, USBSS_SYSCONFIG) & USBSS_SYSCONFIG_SRESET) { - DELAY(100); - if (i-- == 0) { - device_printf(dev, "reset timeout.\n"); - return (ENXIO); - } - } - - /* Read the module revision. */ - rev = USBSS_READ4(sc, USBSS_REVREG); - device_printf(dev, "TI AM335X USBSS v%d.%d.%d\n", - (rev >> 8) & 7, (rev >> 6) & 3, rev & 63); - - node = ofw_bus_get_node(dev); - - if (node == -1) { - usbss_detach(dev); - return (ENXIO); - } - - simplebus_init(dev, node); - - /* - * Allow devices to identify. - */ - bus_generic_probe(dev); - - /* - * Now walk the OFW tree and attach top-level devices. - */ - for (node = OF_child(node); node > 0; node = OF_peer(node)) - simplebus_add_device(dev, node, 0, NULL, -1, NULL); - - return (bus_generic_attach(dev)); -} - -static int -usbss_detach(device_t dev) -{ - struct usbss_softc *sc = device_get_softc(dev); - - /* Free resources if any */ - if (sc->sc_mem_res) - bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, - sc->sc_mem_res); - - /* during module unload there are lots of children leftover */ - device_delete_children(dev); - - return (0); -} - -static device_method_t usbss_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, usbss_probe), - DEVMETHOD(device_attach, usbss_attach), - DEVMETHOD(device_detach, usbss_detach), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - - DEVMETHOD_END -}; - -DEFINE_CLASS_1(usbss, usbss_driver, usbss_methods, - sizeof(struct usbss_softc), simplebus_driver); -static devclass_t usbss_devclass; -DRIVER_MODULE(usbss, simplebus, usbss_driver, usbss_devclass, 0, 0); -MODULE_DEPEND(usbss, usb, 1, 1, 1); Property changes on: head/sys/arm/ti/am335x/am335x_usbss.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/ti/am335x/am3359_cppi41.c =================================================================== --- head/sys/arm/ti/am335x/am3359_cppi41.c (nonexistent) +++ head/sys/arm/ti/am335x/am3359_cppi41.c (revision 363700) @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 2019 Emmanuel Vadot + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +/* Based on sys/arm/ti/ti_sysc.c */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +struct ti_am3359_cppi41_softc { + device_t dev; + struct syscon * syscon; + struct resource * res[4]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct mtx mtx; +}; + +static struct resource_spec ti_am3359_cppi41_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +/* Device */ +static struct ofw_compat_data compat_data[] = { + { "ti,am3359-cppi41", 1 }, + { NULL, 0 } +}; + +static int +ti_am3359_cppi41_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct ti_am3359_cppi41_softc *sc; + + sc = device_get_softc(dev); + DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val); + mtx_lock(&sc->mtx); + bus_space_write_4(sc->bst, sc->bsh, addr, val); + mtx_unlock(&sc->mtx); + return (0); +} + +static uint32_t +ti_am3359_cppi41_read_4(device_t dev, bus_addr_t addr) +{ + struct ti_am3359_cppi41_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); + val = bus_space_read_4(sc->bst, sc->bsh, addr); + mtx_unlock(&sc->mtx); + DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, val); + return (val); +} + +/* device interface */ +static int +ti_am3359_cppi41_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI AM3359 CPPI 41"); + return(BUS_PROBE_DEFAULT); +} + +static int +ti_am3359_cppi41_attach(device_t dev) +{ + struct ti_am3359_cppi41_softc *sc; + phandle_t node; + uint32_t reg, reset_bit, timeout=10; + uint64_t sysc_address; + device_t parent; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, ti_am3359_cppi41_res_spec, sc->res)) { + device_printf(sc->dev, "Cant allocate resources\n"); + return (ENXIO); + } + + sc->dev = dev; + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF); + node = ofw_bus_get_node(sc->dev); + + /* variant of am335x_usbss.c */ + DPRINTF(dev, "-- RESET USB --\n"); + parent = device_get_parent(dev); + reset_bit = ti_sysc_get_soft_reset_bit(parent); + if (reset_bit == 0) { + DPRINTF(dev, "Dont have reset bit\n"); + return (0); + } + sysc_address = ti_sysc_get_sysc_address_offset_host(parent); + DPRINTF(dev, "sysc_address %x\n", sysc_address); + ti_am3359_cppi41_write_4(dev, sysc_address, reset_bit); + DELAY(100); + reg = ti_am3359_cppi41_read_4(dev, sysc_address); + if ((reg & reset_bit) && timeout--) { + DPRINTF(dev, "Reset still ongoing - wait a little bit longer\n"); + DELAY(100); + reg = ti_am3359_cppi41_read_4(dev, sysc_address); + } + if (timeout == 0) + device_printf(dev, "USB Reset timeout\n"); + + return (0); +} + + +static device_method_t ti_am3359_cppi41_methods[] = { + DEVMETHOD(device_probe, ti_am3359_cppi41_probe), + DEVMETHOD(device_attach, ti_am3359_cppi41_attach), + + DEVMETHOD_END +}; + + +DEFINE_CLASS_1(ti_am3359_cppi41, ti_am3359_cppi41_driver, + ti_am3359_cppi41_methods,sizeof(struct ti_am3359_cppi41_softc), + simplebus_driver); + +static devclass_t ti_am3359_cppi41_devclass; + +EARLY_DRIVER_MODULE(ti_am3359_cppi41, simplebus, ti_am3359_cppi41_driver, + ti_am3359_cppi41_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_am3359_cppi41, 1); +MODULE_DEPEND(ti_am3359_cppi41, ti_sysc, 1, 1, 1); Property changes on: head/sys/arm/ti/am335x/am3359_cppi41.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/am335x/am335x_dmtimer.c =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 363700) @@ -1,360 +1,408 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Damjan Marion * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* For arm_set_delay */ +#include + #include #include #include -#include -#include +#include #include "am335x_dmtreg.h" struct am335x_dmtimer_softc { device_t dev; int tmr_mem_rid; struct resource * tmr_mem_res; int tmr_irq_rid; struct resource * tmr_irq_res; void *tmr_irq_handler; - uint32_t sysclk_freq; + clk_t clk_fck; + uint64_t sysclk_freq; uint32_t tclr; /* Cached TCLR register. */ union { struct timecounter tc; struct eventtimer et; } func; int tmr_num; /* Hardware unit number. */ char tmr_name[12]; /* "DMTimerN", N = tmr_num */ }; static struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL; static struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL; static void am335x_dmtimer_delay(int, void *); /* * We use dmtimer2 for eventtimer and dmtimer3 for timecounter. */ #define ET_TMR_NUM 2 #define TC_TMR_NUM 3 /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am335x-timer", 1}, {"ti,am335x-timer-1ms", 1}, {NULL, 0}, }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->tmr_mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->tmr_mem_res, (reg), (val)) static int am335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct am335x_dmtimer_softc *sc; uint32_t initial_count, reload_count; sc = et->et_priv; /* * Stop the timer before changing it. This routine will often be called * while the timer is still running, to either lengthen or shorten the * current event time. We need to ensure the timer doesn't expire while * we're working with it. * * Also clear any pending interrupt status, because it's at least * theoretically possible that we're running in a primary interrupt * context now, and a timer interrupt could be pending even before we * stopped the timer. The more likely case is that we're being called * from the et_event_cb() routine dispatched from our own handler, but * it's not clear to me that that's the only case possible. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (period != 0) { reload_count = ((uint32_t)et->et_frequency * period) >> 32; sc->tclr |= DMT_TCLR_AUTOLOAD; } else { reload_count = 0; } if (first != 0) initial_count = ((uint32_t)et->et_frequency * first) >> 32; else initial_count = reload_count; /* * Set auto-reload and current-count values. This timer hardware counts * up from the initial/reload value and interrupts on the zero rollover. */ DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); /* Enable overflow interrupt, and start the timer. */ DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); sc->tclr |= DMT_TCLR_START; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); return (0); } static int am335x_dmtimer_et_stop(struct eventtimer *et) { struct am335x_dmtimer_softc *sc; sc = et->et_priv; /* Stop timer, disable and clear interrupt. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); return (0); } static int am335x_dmtimer_et_intr(void *arg) { struct am335x_dmtimer_softc *sc; sc = arg; /* Ack the interrupt, and invoke the callback if it's still enabled. */ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (sc->func.et.et_active) sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg); return (FILTER_HANDLED); } static int am335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer")); /* * Setup eventtimer interrupt handling. Panic if anything goes wrong, * because the system just isn't going to run without an eventtimer. */ sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->tmr_irq_rid, RF_ACTIVE); if (sc->tmr_irq_res == NULL) panic("am335x_dmtimer: could not allocate irq resources"); if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK, am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0) panic("am335x_dmtimer: count not setup irq handler"); sc->func.et.et_name = sc->tmr_name; sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->func.et.et_quality = 500; sc->func.et.et_frequency = sc->sysclk_freq; sc->func.et.et_min_period = ((0x00000005LLU << 32) / sc->func.et.et_frequency); sc->func.et.et_max_period = (0xfffffffeLLU << 32) / sc->func.et.et_frequency; sc->func.et.et_start = am335x_dmtimer_et_start; sc->func.et.et_stop = am335x_dmtimer_et_stop; sc->func.et.et_priv = sc; am335x_dmtimer_et_sc = sc; et_register(&sc->func.et); return (0); } static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static int am335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter")); /* Set up timecounter, start it, register it. */ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; DMTIMER_WRITE4(sc, DMT_TLDR, 0); DMTIMER_WRITE4(sc, DMT_TCRR, 0); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); sc->func.tc.tc_name = sc->tmr_name; sc->func.tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; sc->func.tc.tc_counter_mask = ~0u; sc->func.tc.tc_frequency = sc->sysclk_freq; sc->func.tc.tc_quality = 500; sc->func.tc.tc_priv = sc; am335x_dmtimer_tc_sc = sc; tc_init(&sc->func.tc); arm_set_delay(am335x_dmtimer_delay, sc); return (0); } static int am335x_dmtimer_probe(device_t dev) { char strbuf[32]; int tmr_num; + uint64_t rev_address; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* - * Get the hardware unit number (the N from ti,hwmods="timerN"). + * Get the hardware unit number from address of rev register. * If this isn't the hardware unit we're going to use for either the * eventtimer or the timecounter, no point in instantiating the device. */ - tmr_num = ti_hwmods_get_unit(dev, "timer"); - if (tmr_num != ET_TMR_NUM && tmr_num != TC_TMR_NUM) - return (ENXIO); + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + switch (rev_address) { + case DMTIMER2_REV: + tmr_num = 2; + break; + case DMTIMER3_REV: + tmr_num = 3; + break; + default: + /* Not DMTIMER2 or DMTIMER3 */ + return (ENXIO); + } snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num); device_set_desc_copy(dev, strbuf); return(BUS_PROBE_DEFAULT); } static int am335x_dmtimer_attach(device_t dev) { struct am335x_dmtimer_softc *sc; - clk_ident_t timer_id; int err; + uint64_t rev_address; + clk_t sys_clkin; sc = device_get_softc(dev); sc->dev = dev; - /* Get the base clock frequency. */ - if ((err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq)) != 0) - return (err); + /* expect one clock */ + err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck); + if (err != 0) { + device_printf(dev, "Cant find clock index 0. err: %d\n", err); + return (ENXIO); + } + err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin); + if (err != 0) { + device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err); + return (ENXIO); + } + + /* Select M_OSC as DPLL parent */ + err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin); + if (err != 0) { + device_printf(dev, "Cant set mux to CLK_M_OSC\n"); + return (ENXIO); + } + /* Enable clocks and power on the device. */ - if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT) + err = ti_sysc_clock_enable(device_get_parent(dev)); + if (err != 0) { + device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err); return (ENXIO); - if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0) - return (err); - if ((err = ti_prcm_clk_enable(timer_id)) != 0) - return (err); + } + /* Get the base clock frequency. */ + err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq); + if (err != 0) { + device_printf(dev, "Cant get sysclk frequency, err %d\n", err); + return (ENXIO); + } + /* Request the memory resources. */ sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->tmr_mem_rid, RF_ACTIVE); if (sc->tmr_mem_res == NULL) { return (ENXIO); } - sc->tmr_num = ti_hwmods_get_unit(dev, "timer"); + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + switch (rev_address) { + case DMTIMER2_REV: + sc->tmr_num = 2; + break; + case DMTIMER3_REV: + sc->tmr_num = 3; + break; + default: + device_printf(dev, "Not timer 2 or 3! %#jx\n", + rev_address); + return (ENXIO); + } + snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); /* * Go set up either a timecounter or eventtimer. We wouldn't have * attached if we weren't one or the other. */ if (sc->tmr_num == ET_TMR_NUM) am335x_dmtimer_et_init(sc); else if (sc->tmr_num == TC_TMR_NUM) am335x_dmtimer_tc_init(sc); else panic("am335x_dmtimer: bad timer number %d", sc->tmr_num); return (0); } static device_method_t am335x_dmtimer_methods[] = { DEVMETHOD(device_probe, am335x_dmtimer_probe), DEVMETHOD(device_attach, am335x_dmtimer_attach), { 0, 0 } }; static driver_t am335x_dmtimer_driver = { "am335x_dmtimer", am335x_dmtimer_methods, sizeof(struct am335x_dmtimer_softc), }; static devclass_t am335x_dmtimer_devclass; DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0); -MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1); +MODULE_DEPEND(am335x_dmtimer, ti_sysc, 1, 1, 1); static void am335x_dmtimer_delay(int usec, void *arg) { struct am335x_dmtimer_softc *sc = arg; int32_t counts; uint32_t first, last; /* Get the number of times to count */ counts = (usec + 1) * (sc->sysclk_freq / 1000000); first = DMTIMER_READ4(sc, DMT_TCRR); while (counts > 0) { last = DMTIMER_READ4(sc, DMT_TCRR); if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } Index: head/sys/arm/ti/am335x/am335x_dmtpps.c =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtpps.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_dmtpps.c (revision 363700) @@ -1,545 +1,621 @@ /*- * Copyright (c) 2015 Ian lepore * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /* * AM335x PPS driver using DMTimer capture. * * Note that this PPS driver does not use an interrupt. Instead it uses the * hardware's ability to latch the timer's count register in response to a * signal on an IO pin. Each of timers 4-7 have an associated pin, and this * code allows any one of those to be used. * * The timecounter routines in kern_tc.c call the pps poll routine periodically * to see if a new counter value has been latched. When a new value has been * latched, the only processing done in the poll routine is to capture the * current set of timecounter timehands (done with pps_capture()) and the * latched value from the timer. The remaining work (done by pps_event() while * holding a mutex) is scheduled to be done later in a non-interrupt context. */ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include -#include -#include +#include #include #include #include "am335x_dmtreg.h" #define PPS_CDEV_NAME "dmtpps" struct dmtpps_softc { device_t dev; int mem_rid; struct resource * mem_res; int tmr_num; /* N from hwmod str "timerN" */ char tmr_name[12]; /* "DMTimerN" */ uint32_t tclr; /* Cached TCLR register. */ struct timecounter tc; int pps_curmode; /* Edge mode now set in hw. */ struct cdev * pps_cdev; struct pps_state pps_state; struct mtx pps_mtx; + clk_t clk_fck; + uint64_t sysclk_freq; }; static int dmtpps_tmr_num; /* Set by probe() */ /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am335x-timer", 1}, {"ti,am335x-timer-1ms", 1}, {NULL, 0}, }; SIMPLEBUS_PNP_INFO(compat_data); /* * A table relating pad names to the hardware timer number they can be mux'd to. */ struct padinfo { char * ballname; int tmr_num; }; static struct padinfo dmtpps_padinfo[] = { {"GPMC_ADVn_ALE", 4}, {"I2C0_SDA", 4}, {"MII1_TX_EN", 4}, {"XDMA_EVENT_INTR0", 4}, {"GPMC_BEn0_CLE", 5}, {"MDC", 5}, {"MMC0_DAT3", 5}, {"UART1_RTSn", 5}, {"GPMC_WEn", 6}, {"MDIO", 6}, {"MMC0_DAT2", 6}, {"UART1_CTSn", 6}, {"GPMC_OEn_REn", 7}, {"I2C0_SCL", 7}, {"UART0_CTSn", 7}, {"XDMA_EVENT_INTR1", 7}, {NULL, 0} }; /* * This is either brilliantly user-friendly, or utterly lame... * * The am335x chip is used on the popular Beaglebone boards. Those boards have * pins for all four capture-capable timers available on the P8 header. Allow * users to configure the input pin by giving the name of the header pin. */ struct nicknames { const char * nick; const char * name; }; static struct nicknames dmtpps_pin_nicks[] = { {"P8-7", "GPMC_ADVn_ALE"}, {"P8-9", "GPMC_BEn0_CLE"}, {"P8-10", "GPMC_WEn"}, {"P8-8", "GPMC_OEn_REn",}, {NULL, NULL} }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) /* * Translate a short friendly case-insensitive name to its canonical name. */ static const char * dmtpps_translate_nickname(const char *nick) { struct nicknames *nn; for (nn = dmtpps_pin_nicks; nn->nick != NULL; nn++) if (strcasecmp(nick, nn->nick) == 0) return nn->name; return (nick); } /* * See if our tunable is set to the name of the input pin. If not, that's NOT * an error, return 0. If so, try to configure that pin as a timer capture * input pin, and if that works, then we have our timer unit number and if it * fails that IS an error, return -1. */ static int dmtpps_find_tmr_num_by_tunable(void) { struct padinfo *pi; char iname[20]; char muxmode[12]; const char * ballname; int err; if (!TUNABLE_STR_FETCH("hw.am335x_dmtpps.input", iname, sizeof(iname))) return (0); ballname = dmtpps_translate_nickname(iname); for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { if (strcmp(ballname, pi->ballname) != 0) continue; snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); err = ti_pinmux_padconf_set(pi->ballname, muxmode, PADCONF_INPUT); if (err != 0) { printf("am335x_dmtpps: unable to configure capture pin " "for %s to input mode\n", muxmode); return (-1); } else if (bootverbose) { printf("am335x_dmtpps: configured pin %s as input " "for %s\n", iname, muxmode); } return (pi->tmr_num); } /* Invalid name in the tunable, that's an error. */ printf("am335x_dmtpps: unknown pin name '%s'\n", iname); return (-1); } /* * Ask the pinmux driver whether any pin has been configured as a TIMER4..TIMER7 * input pin. If so, return the timer number, if not return 0. */ static int dmtpps_find_tmr_num_by_padconf(void) { int err; unsigned int padstate; const char * padmux; struct padinfo *pi; char muxmode[12]; for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { err = ti_pinmux_padconf_get(pi->ballname, &padmux, &padstate); snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); if (err == 0 && (padstate & RXACTIVE) != 0 && strcmp(muxmode, padmux) == 0) return (pi->tmr_num); } /* Nothing found, not an error. */ return (0); } /* * Figure out which hardware timer number to use based on input pin * configuration. This is done just once, the first time probe() runs. */ static int dmtpps_find_tmr_num(void) { int tmr_num; if ((tmr_num = dmtpps_find_tmr_num_by_tunable()) == 0) tmr_num = dmtpps_find_tmr_num_by_padconf(); if (tmr_num <= 0) { printf("am335x_dmtpps: PPS driver not enabled: unable to find " "or configure a capture input pin\n"); tmr_num = -1; /* Must return non-zero to prevent re-probing. */ } return (tmr_num); } static void dmtpps_set_hw_capture(struct dmtpps_softc *sc, bool force_off) { int newmode; if (force_off) newmode = 0; else newmode = sc->pps_state.ppsparam.mode & PPS_CAPTUREASSERT; if (newmode == sc->pps_curmode) return; sc->pps_curmode = newmode; if (newmode == PPS_CAPTUREASSERT) sc->tclr |= DMT_TCLR_CAPTRAN_LOHI; else sc->tclr &= ~DMT_TCLR_CAPTRAN_MASK; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); } static unsigned dmtpps_get_timecount(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static void dmtpps_poll(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; /* * If a new value has been latched we've got a PPS event. Capture the * timecounter data, then override the capcount field (pps_capture() * populates it from the current DMT_TCRR register) with the latched * value from the TCAR1 register. * * Note that we don't have the TCAR interrupt enabled, but the hardware * still provides the status bits in the "RAW" status register even when * they're masked from generating an irq. However, when clearing the * TCAR status to re-arm the capture for the next second, we have to * write to the IRQ status register, not the RAW register. Quirky. * * We do not need to hold a lock while capturing the pps data, because * it is captured into an area of the pps_state struct which is read * only by pps_event(). We do need to hold a lock while calling * pps_event(), because it manipulates data which is also accessed from * the ioctl(2) context by userland processes. */ if (DMTIMER_READ4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) { pps_capture(&sc->pps_state); sc->pps_state.capcount = DMTIMER_READ4(sc, DMT_TCAR1); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR); mtx_lock_spin(&sc->pps_mtx); pps_event(&sc->pps_state, PPS_CAPTUREASSERT); mtx_unlock_spin(&sc->pps_mtx); } } static int dmtpps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Begin polling for pps and enable capture in the hardware whenever the * device is open. Doing this stuff again is harmless if this isn't the * first open. */ sc->tc.tc_poll_pps = dmtpps_poll; dmtpps_set_hw_capture(sc, false); return 0; } static int dmtpps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Stop polling and disable capture on last close. Use the force-off * flag to override the configured mode and turn off the hardware. */ sc->tc.tc_poll_pps = NULL; dmtpps_set_hw_capture(sc, true); return 0; } static int dmtpps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct dmtpps_softc *sc; int err; sc = dev->si_drv1; /* Let the kernel do the heavy lifting for ioctl. */ mtx_lock_spin(&sc->pps_mtx); err = pps_ioctl(cmd, data, &sc->pps_state); mtx_unlock_spin(&sc->pps_mtx); if (err != 0) return (err); /* * The capture mode could have changed, set the hardware to whatever * mode is now current. Effectively a no-op if nothing changed. */ dmtpps_set_hw_capture(sc, false); return (err); } static struct cdevsw dmtpps_cdevsw = { .d_version = D_VERSION, .d_open = dmtpps_open, .d_close = dmtpps_close, .d_ioctl = dmtpps_ioctl, .d_name = PPS_CDEV_NAME, }; static int dmtpps_probe(device_t dev) { char strbuf[64]; int tmr_num; + uint64_t rev_address; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* * If we haven't chosen which hardware timer to use yet, go do that now. * We need to know that to decide whether to return success for this * hardware timer instance or not. */ if (dmtpps_tmr_num == 0) dmtpps_tmr_num = dmtpps_find_tmr_num(); /* * Figure out which hardware timer is being probed and see if it matches * the configured timer number determined earlier. */ - tmr_num = ti_hwmods_get_unit(dev, "timer"); + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + switch (rev_address) { + case DMTIMER1_1MS_REV: + tmr_num = 1; + break; + case DMTIMER2_REV: + tmr_num = 2; + break; + case DMTIMER3_REV: + tmr_num = 3; + break; + case DMTIMER4_REV: + tmr_num = 4; + break; + case DMTIMER5_REV: + tmr_num = 5; + break; + case DMTIMER6_REV: + tmr_num = 6; + break; + case DMTIMER7_REV: + tmr_num = 7; + break; + default: + return (ENXIO); + } + if (dmtpps_tmr_num != tmr_num) return (ENXIO); snprintf(strbuf, sizeof(strbuf), "AM335x PPS-Capture DMTimer%d", tmr_num); device_set_desc_copy(dev, strbuf); return(BUS_PROBE_DEFAULT); } static int dmtpps_attach(device_t dev) { struct dmtpps_softc *sc; struct make_dev_args mda; - clk_ident_t timer_id; - int err, sysclk_freq; + int err; + clk_t sys_clkin; + uint64_t rev_address; sc = device_get_softc(dev); sc->dev = dev; - /* Get the base clock frequency. */ - err = ti_prcm_clk_get_source_freq(SYS_CLK, &sysclk_freq); + /* Figure out which hardware timer this is and set the name string. */ + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + switch (rev_address) { + case DMTIMER1_1MS_REV: + sc->tmr_num = 1; + break; + case DMTIMER2_REV: + sc->tmr_num = 2; + break; + case DMTIMER3_REV: + sc->tmr_num = 3; + break; + case DMTIMER4_REV: + sc->tmr_num = 4; + break; + case DMTIMER5_REV: + sc->tmr_num = 5; + break; + case DMTIMER6_REV: + sc->tmr_num = 6; + break; + case DMTIMER7_REV: + sc->tmr_num = 7; + break; + } + snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); + /* expect one clock */ + err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_fck); + if (err != 0) { + device_printf(dev, "Cant find clock index 0. err: %d\n", err); + return (ENXIO); + } + + err = clk_get_by_name(dev, "sys_clkin_ck@40", &sys_clkin); + if (err != 0) { + device_printf(dev, "Cant find sys_clkin_ck@40 err: %d\n", err); + return (ENXIO); + } + + /* Select M_OSC as DPLL parent */ + err = clk_set_parent_by_clk(sc->clk_fck, sys_clkin); + if (err != 0) { + device_printf(dev, "Cant set mux to CLK_M_OSC\n"); + return (ENXIO); + } + /* Enable clocks and power on the device. */ - if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT) + err = ti_sysc_clock_enable(device_get_parent(dev)); + if (err != 0) { + device_printf(dev, "Cant enable sysc clkctrl, err %d\n", err); return (ENXIO); - if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0) - return (err); - if ((err = ti_prcm_clk_enable(timer_id)) != 0) - return (err); + } + /* Get the base clock frequency. */ + err = clk_get_freq(sc->clk_fck, &sc->sysclk_freq); + if (err != 0) { + device_printf(dev, "Cant get sysclk frequency, err %d\n", err); + return (ENXIO); + } /* Request the memory resources. */ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) { return (ENXIO); } - /* Figure out which hardware timer this is and set the name string. */ - sc->tmr_num = ti_hwmods_get_unit(dev, "timer"); - snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); - /* * Configure the timer pulse/capture pin to input/capture mode. This is * required in addition to configuring the pin as input with the pinmux * controller (which was done via fdt data or tunable at probe time). */ sc->tclr = DMT_TCLR_GPO_CFG; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); /* Set up timecounter hardware, start it. */ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; DMTIMER_WRITE4(sc, DMT_TLDR, 0); DMTIMER_WRITE4(sc, DMT_TCRR, 0); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); /* Register the timecounter. */ sc->tc.tc_name = sc->tmr_name; sc->tc.tc_get_timecount = dmtpps_get_timecount; sc->tc.tc_counter_mask = ~0u; - sc->tc.tc_frequency = sysclk_freq; + sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* * Indicate our PPS capabilities. Have the kernel init its part of the * pps_state struct and add its capabilities. * * While the hardware has a mode to capture each edge, it's not clear we * can use it that way, because there's only a single interrupt/status * bit to say something was captured, but not which edge it was. For * now, just say we can only capture assert events (the positive-going * edge of the pulse). */ mtx_init(&sc->pps_mtx, "dmtpps", NULL, MTX_SPIN); sc->pps_state.flags = PPSFLAG_MTX_SPIN; sc->pps_state.ppscap = PPS_CAPTUREASSERT; sc->pps_state.driver_abi = PPS_ABI_VERSION; sc->pps_state.driver_mtx = &sc->pps_mtx; pps_init_abi(&sc->pps_state); /* Create the PPS cdev. */ make_dev_args_init(&mda); mda.mda_flags = MAKEDEV_WAITOK; mda.mda_devsw = &dmtpps_cdevsw; mda.mda_cr = NULL; mda.mda_uid = UID_ROOT; mda.mda_gid = GID_WHEEL; mda.mda_mode = 0600; mda.mda_unit = device_get_unit(dev); mda.mda_si_drv1 = sc; if ((err = make_dev_s(&mda, &sc->pps_cdev, PPS_CDEV_NAME)) != 0) { device_printf(dev, "Failed to create cdev %s\n", PPS_CDEV_NAME); return (err); } if (bootverbose) device_printf(sc->dev, "Using %s for PPS device /dev/%s\n", sc->tmr_name, PPS_CDEV_NAME); return (0); } static int dmtpps_detach(device_t dev) { /* * There is no way to remove a timecounter once it has been registered, * even if it's not in use, so we can never detach. If we were * dynamically loaded as a module this will prevent unloading. */ return (EBUSY); } static device_method_t dmtpps_methods[] = { DEVMETHOD(device_probe, dmtpps_probe), DEVMETHOD(device_attach, dmtpps_attach), DEVMETHOD(device_detach, dmtpps_detach), { 0, 0 } }; static driver_t dmtpps_driver = { "am335x_dmtpps", dmtpps_methods, sizeof(struct dmtpps_softc), }; static devclass_t dmtpps_devclass; DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, dmtpps_devclass, 0, 0); -MODULE_DEPEND(am335x_dmtpps, am335x_prcm, 1, 1, 1); - +MODULE_DEPEND(am335x_dmtpps, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_dmtreg.h =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtreg.h (revision 363699) +++ head/sys/arm/ti/am335x/am335x_dmtreg.h (revision 363700) @@ -1,76 +1,88 @@ /*- * Copyright (c) 2012 Damjan Marion * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD$ */ #ifndef AM335X_DMTREG_H #define AM335X_DMTREG_H #define AM335X_NUM_TIMERS 8 #define DMT_TIDR 0x00 /* Identification Register */ #define DMT_TIOCP_CFG 0x10 /* OCP Configuration Reg */ #define DMT_TIOCP_RESET (1 << 0) /* TIOCP perform soft reset */ #define DMT_IQR_EOI 0x20 /* IRQ End-Of-Interrupt Reg */ #define DMT_IRQSTATUS_RAW 0x24 /* IRQSTATUS Raw Reg */ #define DMT_IRQSTATUS 0x28 /* IRQSTATUS Reg */ #define DMT_IRQENABLE_SET 0x2c /* IRQSTATUS Set Reg */ #define DMT_IRQENABLE_CLR 0x30 /* IRQSTATUS Clear Reg */ #define DMT_IRQWAKEEN 0x34 /* IRQ Wakeup Enable Reg */ #define DMT_IRQ_MAT (1 << 0) /* IRQ: Match */ #define DMT_IRQ_OVF (1 << 1) /* IRQ: Overflow */ #define DMT_IRQ_TCAR (1 << 2) /* IRQ: Capture */ #define DMT_IRQ_MASK (DMT_IRQ_TCAR | DMT_IRQ_OVF | DMT_IRQ_MAT) #define DMT_TCLR 0x38 /* Control Register */ #define DMT_TCLR_START (1 << 0) /* Start timer */ #define DMT_TCLR_AUTOLOAD (1 << 1) /* Auto-reload on overflow */ #define DMT_TCLR_PRES_MASK (7 << 2) /* Prescaler mask */ #define DMT_TCLR_PRES_ENABLE (1 << 5) /* Prescaler enable */ #define DMT_TCLR_COMP_ENABLE (1 << 6) /* Compare enable */ #define DMT_TCLR_PWM_HIGH (1 << 7) /* PWM default output high */ #define DMT_TCLR_CAPTRAN_MASK (3 << 8) /* Capture transition mask */ #define DMT_TCLR_CAPTRAN_NONE (0 << 8) /* Capture: none */ #define DMT_TCLR_CAPTRAN_LOHI (1 << 8) /* Capture lo->hi transition */ #define DMT_TCLR_CAPTRAN_HILO (2 << 8) /* Capture hi->lo transition */ #define DMT_TCLR_CAPTRAN_BOTH (3 << 8) /* Capture both transitions */ #define DMT_TCLR_TRGMODE_MASK (3 << 10) /* Trigger output mode mask */ #define DMT_TCLR_TRGMODE_NONE (0 << 10) /* Trigger off */ #define DMT_TCLR_TRGMODE_OVFL (1 << 10) /* Trigger on overflow */ #define DMT_TCLR_TRGMODE_BOTH (2 << 10) /* Trigger on match + ovflow */ #define DMT_TCLR_PWM_PTOGGLE (1 << 12) /* PWM toggles */ #define DMT_TCLR_CAP_MODE_2ND (1 << 13) /* Capture second event mode */ #define DMT_TCLR_GPO_CFG (1 << 14) /* Tmr pin conf, 0=out, 1=in */ #define DMT_TCRR 0x3C /* Counter Register */ #define DMT_TLDR 0x40 /* Load Reg */ #define DMT_TTGR 0x44 /* Trigger Reg */ #define DMT_TWPS 0x48 /* Write Posted Status Reg */ #define DMT_TMAR 0x4C /* Match Reg */ #define DMT_TCAR1 0x50 /* Capture Reg */ #define DMT_TSICR 0x54 /* Synchr. Interface Ctrl Reg */ #define DMT_TSICR_RESET (1 << 1) /* TSICR perform soft reset */ #define DMT_TCAR2 0x48 /* Capture Reg */ +/* Location of revision register from TRM Memory map chapter 2 */ +/* L4_WKUP */ +#define DMTIMER0_REV 0x05000 +#define DMTIMER1_1MS_REV 0x31000 +/* L4_PER */ +#define DMTIMER2_REV 0x40000 +#define DMTIMER3_REV 0x42000 +#define DMTIMER4_REV 0x44000 +#define DMTIMER5_REV 0x46000 +#define DMTIMER6_REV 0x48000 +#define DMTIMER7_REV 0x4A000 + #endif /* AM335X_DMTREG_H */ Index: head/sys/arm/ti/am335x/am335x_gpio.c =================================================================== --- head/sys/arm/ti/am335x/am335x_gpio.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_gpio.c (revision 363700) @@ -1,157 +1,158 @@ /*- * Copyright (c) 2012 Damjan Marion * Copyright (c) 2014 Andrew Turner * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ti_gpio_if.h" static struct ofw_compat_data compat_data[] = { {"ti,am335x-gpio", 1}, /* Linux uses ti,omap4-gpio on am335x so we need to support it */ {"ti,omap4-gpio", 1}, {"ti,gpio", 1}, {NULL, 0}, }; static int am335x_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); if (ti_chip() != CHIP_AM335X) return (ENXIO); device_set_desc(dev, "TI AM335x General Purpose I/O (GPIO)"); return (0); } static int am335x_gpio_set_flags(device_t dev, uint32_t gpio, uint32_t flags) { unsigned int state = 0; struct ti_gpio_softc *sc = device_get_softc(dev); if (flags & GPIO_PIN_OUTPUT) { if (flags & GPIO_PIN_PULLUP) state = PADCONF_OUTPUT_PULLUP; else state = PADCONF_OUTPUT; } else if (flags & GPIO_PIN_INPUT) { if (flags & GPIO_PIN_PULLUP) state = PADCONF_INPUT_PULLUP; else if (flags & GPIO_PIN_PULLDOWN) state = PADCONF_INPUT_PULLDOWN; else state = PADCONF_INPUT; } return ti_pinmux_padconf_set_gpiomode(sc->sc_bank*32 + gpio, state); } static int am335x_gpio_get_flags(device_t dev, uint32_t gpio, uint32_t *flags) { unsigned int state; struct ti_gpio_softc *sc = device_get_softc(dev); if (ti_pinmux_padconf_get_gpiomode(sc->sc_bank*32 + gpio, &state) != 0) { *flags = 0; return (EINVAL); } else { switch (state) { case PADCONF_OUTPUT: *flags = GPIO_PIN_OUTPUT; break; case PADCONF_OUTPUT_PULLUP: *flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP; break; case PADCONF_INPUT: *flags = GPIO_PIN_INPUT; break; case PADCONF_INPUT_PULLUP: *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; break; case PADCONF_INPUT_PULLDOWN: *flags = GPIO_PIN_INPUT | GPIO_PIN_PULLDOWN; break; default: *flags = 0; break; } } return (0); } static device_method_t am335x_gpio_methods[] = { /* bus interface */ DEVMETHOD(device_probe, am335x_gpio_probe), /* ti_gpio interface */ DEVMETHOD(ti_gpio_set_flags, am335x_gpio_set_flags), DEVMETHOD(ti_gpio_get_flags, am335x_gpio_get_flags), DEVMETHOD_END }; extern driver_t ti_gpio_driver; static devclass_t am335x_gpio_devclass; DEFINE_CLASS_1(gpio, am335x_gpio_driver, am335x_gpio_methods, sizeof(struct ti_gpio_softc), ti_gpio_driver); DRIVER_MODULE(am335x_gpio, simplebus, am335x_gpio_driver, am335x_gpio_devclass, 0, 0); +MODULE_DEPEND(am335x_gpio, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_lcd.c =================================================================== --- head/sys/arm/ti/am335x/am335x_lcd.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_lcd.c (revision 363700) @@ -1,1083 +1,1104 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2013 Oleksandr Tymoshenko * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include "opt_syscons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #include #include #include #include #include #include #include #ifdef DEV_SC #include #else /* VT */ #include #endif -#include +#include #include #include "am335x_lcd.h" #include "am335x_pwm.h" #include "fb_if.h" #include "hdmi_if.h" #define LCD_PID 0x00 #define LCD_CTRL 0x04 #define CTRL_DIV_MASK 0xff #define CTRL_DIV_SHIFT 8 #define CTRL_AUTO_UFLOW_RESTART (1 << 1) #define CTRL_RASTER_MODE 1 #define CTRL_LIDD_MODE 0 #define LCD_LIDD_CTRL 0x0C #define LCD_LIDD_CS0_CONF 0x10 #define LCD_LIDD_CS0_ADDR 0x14 #define LCD_LIDD_CS0_DATA 0x18 #define LCD_LIDD_CS1_CONF 0x1C #define LCD_LIDD_CS1_ADDR 0x20 #define LCD_LIDD_CS1_DATA 0x24 #define LCD_RASTER_CTRL 0x28 #define RASTER_CTRL_TFT24_UNPACKED (1 << 26) #define RASTER_CTRL_TFT24 (1 << 25) #define RASTER_CTRL_STN565 (1 << 24) #define RASTER_CTRL_TFTPMAP (1 << 23) #define RASTER_CTRL_NIBMODE (1 << 22) #define RASTER_CTRL_PALMODE_SHIFT 20 #define PALETTE_PALETTE_AND_DATA 0x00 #define PALETTE_PALETTE_ONLY 0x01 #define PALETTE_DATA_ONLY 0x02 #define RASTER_CTRL_REQDLY_SHIFT 12 #define RASTER_CTRL_MONO8B (1 << 9) #define RASTER_CTRL_RBORDER (1 << 8) #define RASTER_CTRL_LCDTFT (1 << 7) #define RASTER_CTRL_LCDBW (1 << 1) #define RASTER_CTRL_LCDEN (1 << 0) #define LCD_RASTER_TIMING_0 0x2C #define RASTER_TIMING_0_HBP_SHIFT 24 #define RASTER_TIMING_0_HFP_SHIFT 16 #define RASTER_TIMING_0_HSW_SHIFT 10 #define RASTER_TIMING_0_PPLLSB_SHIFT 4 #define RASTER_TIMING_0_PPLMSB_SHIFT 3 #define LCD_RASTER_TIMING_1 0x30 #define RASTER_TIMING_1_VBP_SHIFT 24 #define RASTER_TIMING_1_VFP_SHIFT 16 #define RASTER_TIMING_1_VSW_SHIFT 10 #define RASTER_TIMING_1_LPP_SHIFT 0 #define LCD_RASTER_TIMING_2 0x34 #define RASTER_TIMING_2_HSWHI_SHIFT 27 #define RASTER_TIMING_2_LPP_B10_SHIFT 26 #define RASTER_TIMING_2_PHSVS (1 << 25) #define RASTER_TIMING_2_PHSVS_RISE (1 << 24) #define RASTER_TIMING_2_PHSVS_FALL (0 << 24) #define RASTER_TIMING_2_IOE (1 << 23) #define RASTER_TIMING_2_IPC (1 << 22) #define RASTER_TIMING_2_IHS (1 << 21) #define RASTER_TIMING_2_IVS (1 << 20) #define RASTER_TIMING_2_ACBI_SHIFT 16 #define RASTER_TIMING_2_ACB_SHIFT 8 #define RASTER_TIMING_2_HBPHI_SHIFT 4 #define RASTER_TIMING_2_HFPHI_SHIFT 0 #define LCD_RASTER_SUBPANEL 0x38 #define LCD_RASTER_SUBPANEL2 0x3C #define LCD_LCDDMA_CTRL 0x40 #define LCDDMA_CTRL_DMA_MASTER_PRIO_SHIFT 16 #define LCDDMA_CTRL_TH_FIFO_RDY_SHIFT 8 #define LCDDMA_CTRL_BURST_SIZE_SHIFT 4 #define LCDDMA_CTRL_BYTES_SWAP (1 << 3) #define LCDDMA_CTRL_BE (1 << 1) #define LCDDMA_CTRL_FB0_ONLY 0 #define LCDDMA_CTRL_FB0_FB1 (1 << 0) #define LCD_LCDDMA_FB0_BASE 0x44 #define LCD_LCDDMA_FB0_CEILING 0x48 #define LCD_LCDDMA_FB1_BASE 0x4C #define LCD_LCDDMA_FB1_CEILING 0x50 #define LCD_SYSCONFIG 0x54 #define SYSCONFIG_STANDBY_FORCE (0 << 4) #define SYSCONFIG_STANDBY_NONE (1 << 4) #define SYSCONFIG_STANDBY_SMART (2 << 4) #define SYSCONFIG_IDLE_FORCE (0 << 2) #define SYSCONFIG_IDLE_NONE (1 << 2) #define SYSCONFIG_IDLE_SMART (2 << 2) #define LCD_IRQSTATUS_RAW 0x58 #define LCD_IRQSTATUS 0x5C #define LCD_IRQENABLE_SET 0x60 #define LCD_IRQENABLE_CLEAR 0x64 #define IRQ_EOF1 (1 << 9) #define IRQ_EOF0 (1 << 8) #define IRQ_PL (1 << 6) #define IRQ_FUF (1 << 5) #define IRQ_ACB (1 << 3) #define IRQ_SYNC_LOST (1 << 2) #define IRQ_RASTER_DONE (1 << 1) #define IRQ_FRAME_DONE (1 << 0) #define LCD_END_OF_INT_IND 0x68 #define LCD_CLKC_ENABLE 0x6C #define CLKC_ENABLE_DMA (1 << 2) #define CLKC_ENABLE_LDID (1 << 1) #define CLKC_ENABLE_CORE (1 << 0) #define LCD_CLKC_RESET 0x70 #define CLKC_RESET_MAIN (1 << 3) #define CLKC_RESET_DMA (1 << 2) #define CLKC_RESET_LDID (1 << 1) #define CLKC_RESET_CORE (1 << 0) #define LCD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define LCD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define LCD_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "am335x_lcd", MTX_DEF) #define LCD_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx); #define LCD_READ4(_sc, reg) bus_read_4((_sc)->sc_mem_res, reg); #define LCD_WRITE4(_sc, reg, value) \ bus_write_4((_sc)->sc_mem_res, reg, value); /* Backlight is controlled by eCAS interface on PWM unit 0 */ #define PWM_UNIT 0 #define PWM_PERIOD 100 #define MODE_HBP(mode) ((mode)->htotal - (mode)->hsync_end) #define MODE_HFP(mode) ((mode)->hsync_start - (mode)->hdisplay) #define MODE_HSW(mode) ((mode)->hsync_end - (mode)->hsync_start) #define MODE_VBP(mode) ((mode)->vtotal - (mode)->vsync_end) #define MODE_VFP(mode) ((mode)->vsync_start - (mode)->vdisplay) #define MODE_VSW(mode) ((mode)->vsync_end - (mode)->vsync_start) #define MAX_PIXEL_CLOCK 126000 #define MAX_BANDWIDTH (1280*1024*60) struct am335x_lcd_softc { device_t sc_dev; struct fb_info sc_fb_info; struct resource *sc_mem_res; struct resource *sc_irq_res; void *sc_intr_hl; struct mtx sc_mtx; int sc_backlight; struct sysctl_oid *sc_oid; struct panel_info sc_panel; /* Framebuffer */ bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; size_t sc_fb_size; bus_addr_t sc_fb_phys; uint8_t *sc_fb_base; /* HDMI framer */ phandle_t sc_hdmi_framer; eventhandler_tag sc_hdmi_evh; + + /* Clock */ + clk_t sc_clk_dpll_disp_ck; }; static void am335x_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { bus_addr_t *addr; if (err) return; addr = (bus_addr_t*)arg; *addr = segs[0].ds_addr; } static uint32_t am335x_lcd_calc_divisor(uint32_t reference, uint32_t freq) { uint32_t div, i; uint32_t delta, min_delta; min_delta = freq; div = 255; /* Raster mode case: divisors are in range from 2 to 255 */ for (i = 2; i < 255; i++) { delta = abs(reference/i - freq); if (delta < min_delta) { div = i; min_delta = delta; } } return (div); } static int am335x_lcd_sysctl_backlight(SYSCTL_HANDLER_ARGS) { struct am335x_lcd_softc *sc = (struct am335x_lcd_softc*)arg1; int error; int backlight; backlight = sc->sc_backlight; error = sysctl_handle_int(oidp, &backlight, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (backlight < 0) backlight = 0; if (backlight > 100) backlight = 100; LCD_LOCK(sc); error = am335x_pwm_config_ecap(PWM_UNIT, PWM_PERIOD, backlight*PWM_PERIOD/100); if (error == 0) sc->sc_backlight = backlight; LCD_UNLOCK(sc); return (error); } static uint32_t am335x_mode_vrefresh(const struct videomode *mode) { uint32_t refresh; /* Calculate vertical refresh rate */ refresh = (mode->dot_clock * 1000 / mode->htotal); refresh = (refresh + mode->vtotal / 2) / mode->vtotal; if (mode->flags & VID_INTERLACE) refresh *= 2; if (mode->flags & VID_DBLSCAN) refresh /= 2; return refresh; } static int am335x_mode_is_valid(const struct videomode *mode) { uint32_t hbp, hfp, hsw; uint32_t vbp, vfp, vsw; if (mode->dot_clock > MAX_PIXEL_CLOCK) return (0); if (mode->hdisplay & 0xf) return (0); if (mode->vdisplay > 2048) return (0); /* Check ranges for timing parameters */ hbp = MODE_HBP(mode) - 1; hfp = MODE_HFP(mode) - 1; hsw = MODE_HSW(mode) - 1; vbp = MODE_VBP(mode); vfp = MODE_VFP(mode); vsw = MODE_VSW(mode) - 1; if (hbp > 0x3ff) return (0); if (hfp > 0x3ff) return (0); if (hsw > 0x3ff) return (0); if (vbp > 0xff) return (0); if (vfp > 0xff) return (0); if (vsw > 0x3f) return (0); if (mode->vdisplay*mode->hdisplay*am335x_mode_vrefresh(mode) > MAX_BANDWIDTH) return (0); return (1); } static void am335x_read_hdmi_property(device_t dev) { phandle_t node, xref; phandle_t endpoint; phandle_t hdmi_xref; struct am335x_lcd_softc *sc; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); sc->sc_hdmi_framer = 0; /* * Old FreeBSD way of referencing to HDMI framer */ if (OF_getencprop(node, "hdmi", &hdmi_xref, sizeof(hdmi_xref)) != -1) { sc->sc_hdmi_framer = hdmi_xref; return; } /* * Use bindings described in Linux docs: * bindings/media/video-interfaces.txt * We assume that the only endpoint in LCDC node * is HDMI framer. */ node = ofw_bus_find_child(node, "port"); /* No media bindings */ if (node == 0) return; for (endpoint = OF_child(node); endpoint != 0; endpoint = OF_peer(endpoint)) { if (OF_getencprop(endpoint, "remote-endpoint", &xref, sizeof(xref)) != -1) { /* port/port@0/endpoint@0 */ node = OF_node_from_xref(xref); /* port/port@0 */ node = OF_parent(node); /* port */ node = OF_parent(node); /* actual owner of port, in our case HDMI framer */ sc->sc_hdmi_framer = OF_xref_from_node(OF_parent(node)); if (sc->sc_hdmi_framer != 0) return; } } } static int am335x_read_property(device_t dev, phandle_t node, const char *name, uint32_t *val) { pcell_t cell; if ((OF_getencprop(node, name, &cell, sizeof(cell))) <= 0) { device_printf(dev, "missing '%s' attribute in LCD panel info\n", name); return (ENXIO); } *val = cell; return (0); } static int am335x_read_timing(device_t dev, phandle_t node, struct panel_info *panel) { int error; phandle_t timings_node, timing_node, native; timings_node = ofw_bus_find_child(node, "display-timings"); if (timings_node == 0) { device_printf(dev, "no \"display-timings\" node\n"); return (-1); } if (OF_searchencprop(timings_node, "native-mode", &native, sizeof(native)) == -1) { device_printf(dev, "no \"native-mode\" reference in \"timings\" node\n"); return (-1); } timing_node = OF_node_from_xref(native); error = 0; if ((error = am335x_read_property(dev, timing_node, "hactive", &panel->panel_width))) goto out; if ((error = am335x_read_property(dev, timing_node, "vactive", &panel->panel_height))) goto out; if ((error = am335x_read_property(dev, timing_node, "hfront-porch", &panel->panel_hfp))) goto out; if ((error = am335x_read_property(dev, timing_node, "hback-porch", &panel->panel_hbp))) goto out; if ((error = am335x_read_property(dev, timing_node, "hsync-len", &panel->panel_hsw))) goto out; if ((error = am335x_read_property(dev, timing_node, "vfront-porch", &panel->panel_vfp))) goto out; if ((error = am335x_read_property(dev, timing_node, "vback-porch", &panel->panel_vbp))) goto out; if ((error = am335x_read_property(dev, timing_node, "vsync-len", &panel->panel_vsw))) goto out; if ((error = am335x_read_property(dev, timing_node, "clock-frequency", &panel->panel_pxl_clk))) goto out; if ((error = am335x_read_property(dev, timing_node, "pixelclk-active", &panel->pixelclk_active))) goto out; if ((error = am335x_read_property(dev, timing_node, "hsync-active", &panel->hsync_active))) goto out; if ((error = am335x_read_property(dev, timing_node, "vsync-active", &panel->vsync_active))) goto out; out: return (error); } static int am335x_read_panel_info(device_t dev, phandle_t node, struct panel_info *panel) { phandle_t panel_info_node; panel_info_node = ofw_bus_find_child(node, "panel-info"); if (panel_info_node == 0) return (-1); am335x_read_property(dev, panel_info_node, "ac-bias", &panel->ac_bias); am335x_read_property(dev, panel_info_node, "ac-bias-intrpt", &panel->ac_bias_intrpt); am335x_read_property(dev, panel_info_node, "dma-burst-sz", &panel->dma_burst_sz); am335x_read_property(dev, panel_info_node, "bpp", &panel->bpp); am335x_read_property(dev, panel_info_node, "fdd", &panel->fdd); am335x_read_property(dev, panel_info_node, "sync-edge", &panel->sync_edge); am335x_read_property(dev, panel_info_node, "sync-ctrl", &panel->sync_ctrl); return (0); } static void am335x_lcd_intr(void *arg) { struct am335x_lcd_softc *sc = arg; uint32_t reg; reg = LCD_READ4(sc, LCD_IRQSTATUS); LCD_WRITE4(sc, LCD_IRQSTATUS, reg); /* Read value back to make sure it reached the hardware */ reg = LCD_READ4(sc, LCD_IRQSTATUS); if (reg & IRQ_SYNC_LOST) { reg = LCD_READ4(sc, LCD_RASTER_CTRL); reg &= ~RASTER_CTRL_LCDEN; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); reg = LCD_READ4(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); goto done; } if (reg & IRQ_PL) { reg = LCD_READ4(sc, LCD_RASTER_CTRL); reg &= ~RASTER_CTRL_LCDEN; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); reg = LCD_READ4(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); goto done; } if (reg & IRQ_EOF0) { LCD_WRITE4(sc, LCD_LCDDMA_FB0_BASE, sc->sc_fb_phys); LCD_WRITE4(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1); reg &= ~IRQ_EOF0; } if (reg & IRQ_EOF1) { LCD_WRITE4(sc, LCD_LCDDMA_FB1_BASE, sc->sc_fb_phys); LCD_WRITE4(sc, LCD_LCDDMA_FB1_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1); reg &= ~IRQ_EOF1; } if (reg & IRQ_FUF) { /* TODO: Handle FUF */ } if (reg & IRQ_ACB) { /* TODO: Handle ACB */ } done: LCD_WRITE4(sc, LCD_END_OF_INT_IND, 0); /* Read value back to make sure it reached the hardware */ reg = LCD_READ4(sc, LCD_END_OF_INT_IND); } static const struct videomode * am335x_lcd_pick_mode(struct edid_info *ei) { const struct videomode *videomode; const struct videomode *m; int n; /* Get standard VGA as default */ videomode = NULL; /* * Pick a mode. */ if (ei->edid_preferred_mode != NULL) { if (am335x_mode_is_valid(ei->edid_preferred_mode)) videomode = ei->edid_preferred_mode; } if (videomode == NULL) { m = ei->edid_modes; sort_modes(ei->edid_modes, &ei->edid_preferred_mode, ei->edid_nmodes); for (n = 0; n < ei->edid_nmodes; n++) if (am335x_mode_is_valid(&m[n])) { videomode = &m[n]; break; } } return videomode; } static int am335x_lcd_configure(struct am335x_lcd_softc *sc) { int div; uint32_t reg, timing0, timing1, timing2; uint32_t burst_log; size_t dma_size; uint32_t hbp, hfp, hsw; uint32_t vbp, vfp, vsw; uint32_t width, height; - unsigned int ref_freq; + uint64_t ref_freq; int err; /* * try to adjust clock to get double of requested frequency * HDMI/DVI displays are very sensitive to error in frequncy value */ - if (ti_prcm_clk_set_source_freq(LCDC_CLK, sc->sc_panel.panel_pxl_clk*2)) { + + err = clk_set_freq(sc->sc_clk_dpll_disp_ck, sc->sc_panel.panel_pxl_clk*2, + CLK_SET_ROUND_ANY); + if (err != 0) { device_printf(sc->sc_dev, "can't set source frequency\n"); return (ENXIO); } - if (ti_prcm_clk_get_source_freq(LCDC_CLK, &ref_freq)) { + err = clk_get_freq(sc->sc_clk_dpll_disp_ck, &ref_freq); + if (err != 0) { device_printf(sc->sc_dev, "can't get reference frequency\n"); return (ENXIO); } - /* Panle initialization */ + /* Panel initialization */ dma_size = round_page(sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8); /* * Now allocate framebuffer memory */ err = bus_dma_tag_create( bus_get_dma_tag(sc->sc_dev), 4, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ dma_size, 1, /* maxsize, nsegments */ dma_size, 0, /* maxsegsize, flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->sc_dma_tag); if (err) goto done; err = bus_dmamem_alloc(sc->sc_dma_tag, (void **)&sc->sc_fb_base, BUS_DMA_COHERENT, &sc->sc_dma_map); if (err) { device_printf(sc->sc_dev, "cannot allocate framebuffer\n"); goto done; } err = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, sc->sc_fb_base, dma_size, am335x_fb_dmamap_cb, &sc->sc_fb_phys, BUS_DMA_NOWAIT); if (err) { device_printf(sc->sc_dev, "cannot load DMA map\n"); goto done; } /* Make sure it's blank */ memset(sc->sc_fb_base, 0x0, dma_size); /* Calculate actual FB Size */ sc->sc_fb_size = sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8; /* Only raster mode is supported */ reg = CTRL_RASTER_MODE; div = am335x_lcd_calc_divisor(ref_freq, sc->sc_panel.panel_pxl_clk); reg |= (div << CTRL_DIV_SHIFT); LCD_WRITE4(sc, LCD_CTRL, reg); /* Set timing */ timing0 = timing1 = timing2 = 0; hbp = sc->sc_panel.panel_hbp - 1; hfp = sc->sc_panel.panel_hfp - 1; hsw = sc->sc_panel.panel_hsw - 1; vbp = sc->sc_panel.panel_vbp; vfp = sc->sc_panel.panel_vfp; vsw = sc->sc_panel.panel_vsw - 1; height = sc->sc_panel.panel_height - 1; width = sc->sc_panel.panel_width - 1; /* Horizontal back porch */ timing0 |= (hbp & 0xff) << RASTER_TIMING_0_HBP_SHIFT; timing2 |= ((hbp >> 8) & 3) << RASTER_TIMING_2_HBPHI_SHIFT; /* Horizontal front porch */ timing0 |= (hfp & 0xff) << RASTER_TIMING_0_HFP_SHIFT; timing2 |= ((hfp >> 8) & 3) << RASTER_TIMING_2_HFPHI_SHIFT; /* Horizontal sync width */ timing0 |= (hsw & 0x3f) << RASTER_TIMING_0_HSW_SHIFT; timing2 |= ((hsw >> 6) & 0xf) << RASTER_TIMING_2_HSWHI_SHIFT; /* Vertical back porch, front porch, sync width */ timing1 |= (vbp & 0xff) << RASTER_TIMING_1_VBP_SHIFT; timing1 |= (vfp & 0xff) << RASTER_TIMING_1_VFP_SHIFT; timing1 |= (vsw & 0x3f) << RASTER_TIMING_1_VSW_SHIFT; /* Pixels per line */ timing0 |= ((width >> 10) & 1) << RASTER_TIMING_0_PPLMSB_SHIFT; timing0 |= ((width >> 4) & 0x3f) << RASTER_TIMING_0_PPLLSB_SHIFT; /* Lines per panel */ timing1 |= (height & 0x3ff) << RASTER_TIMING_1_LPP_SHIFT; timing2 |= ((height >> 10 ) & 1) << RASTER_TIMING_2_LPP_B10_SHIFT; /* clock signal settings */ if (sc->sc_panel.sync_ctrl) timing2 |= RASTER_TIMING_2_PHSVS; if (sc->sc_panel.sync_edge) timing2 |= RASTER_TIMING_2_PHSVS_RISE; else timing2 |= RASTER_TIMING_2_PHSVS_FALL; if (sc->sc_panel.hsync_active == 0) timing2 |= RASTER_TIMING_2_IHS; if (sc->sc_panel.vsync_active == 0) timing2 |= RASTER_TIMING_2_IVS; if (sc->sc_panel.pixelclk_active == 0) timing2 |= RASTER_TIMING_2_IPC; /* AC bias */ timing2 |= (sc->sc_panel.ac_bias << RASTER_TIMING_2_ACB_SHIFT); timing2 |= (sc->sc_panel.ac_bias_intrpt << RASTER_TIMING_2_ACBI_SHIFT); LCD_WRITE4(sc, LCD_RASTER_TIMING_0, timing0); LCD_WRITE4(sc, LCD_RASTER_TIMING_1, timing1); LCD_WRITE4(sc, LCD_RASTER_TIMING_2, timing2); /* DMA settings */ reg = LCDDMA_CTRL_FB0_FB1; /* Find power of 2 for current burst size */ switch (sc->sc_panel.dma_burst_sz) { case 1: burst_log = 0; break; case 2: burst_log = 1; break; case 4: burst_log = 2; break; case 8: burst_log = 3; break; case 16: default: burst_log = 4; break; } reg |= (burst_log << LCDDMA_CTRL_BURST_SIZE_SHIFT); /* XXX: FIFO TH */ reg |= (0 << LCDDMA_CTRL_TH_FIFO_RDY_SHIFT); LCD_WRITE4(sc, LCD_LCDDMA_CTRL, reg); LCD_WRITE4(sc, LCD_LCDDMA_FB0_BASE, sc->sc_fb_phys); LCD_WRITE4(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1); LCD_WRITE4(sc, LCD_LCDDMA_FB1_BASE, sc->sc_fb_phys); LCD_WRITE4(sc, LCD_LCDDMA_FB1_CEILING, sc->sc_fb_phys + sc->sc_fb_size - 1); /* Enable LCD */ reg = RASTER_CTRL_LCDTFT; reg |= (sc->sc_panel.fdd << RASTER_CTRL_REQDLY_SHIFT); reg |= (PALETTE_DATA_ONLY << RASTER_CTRL_PALMODE_SHIFT); if (sc->sc_panel.bpp >= 24) reg |= RASTER_CTRL_TFT24; if (sc->sc_panel.bpp == 32) reg |= RASTER_CTRL_TFT24_UNPACKED; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); LCD_WRITE4(sc, LCD_CLKC_ENABLE, CLKC_ENABLE_DMA | CLKC_ENABLE_LDID | CLKC_ENABLE_CORE); LCD_WRITE4(sc, LCD_CLKC_RESET, CLKC_RESET_MAIN); DELAY(100); LCD_WRITE4(sc, LCD_CLKC_RESET, 0); reg = IRQ_EOF1 | IRQ_EOF0 | IRQ_FUF | IRQ_PL | IRQ_ACB | IRQ_SYNC_LOST | IRQ_RASTER_DONE | IRQ_FRAME_DONE; LCD_WRITE4(sc, LCD_IRQENABLE_SET, reg); reg = LCD_READ4(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); LCD_WRITE4(sc, LCD_SYSCONFIG, SYSCONFIG_STANDBY_SMART | SYSCONFIG_IDLE_SMART); sc->sc_fb_info.fb_name = device_get_nameunit(sc->sc_dev); sc->sc_fb_info.fb_vbase = (intptr_t)sc->sc_fb_base; sc->sc_fb_info.fb_pbase = sc->sc_fb_phys; sc->sc_fb_info.fb_size = sc->sc_fb_size; sc->sc_fb_info.fb_bpp = sc->sc_fb_info.fb_depth = sc->sc_panel.bpp; sc->sc_fb_info.fb_stride = sc->sc_panel.panel_width*sc->sc_panel.bpp / 8; sc->sc_fb_info.fb_width = sc->sc_panel.panel_width; sc->sc_fb_info.fb_height = sc->sc_panel.panel_height; #ifdef DEV_SC err = (sc_attach_unit(device_get_unit(sc->sc_dev), device_get_flags(sc->sc_dev) | SC_AUTODETECT_KBD)); if (err) { device_printf(sc->sc_dev, "failed to attach syscons\n"); goto fail; } am335x_lcd_syscons_setup((vm_offset_t)sc->sc_fb_base, sc->sc_fb_phys, &panel); #else /* VT */ device_t fbd = device_add_child(sc->sc_dev, "fbd", device_get_unit(sc->sc_dev)); if (fbd != NULL) { if (device_probe_and_attach(fbd) != 0) device_printf(sc->sc_dev, "failed to attach fbd device\n"); } else device_printf(sc->sc_dev, "failed to add fbd child\n"); #endif done: return (err); } static void am335x_lcd_hdmi_event(void *arg, device_t hdmi, int event) { struct am335x_lcd_softc *sc; const struct videomode *videomode; struct videomode hdmi_mode; device_t hdmi_dev; uint8_t *edid; uint32_t edid_len; struct edid_info ei; sc = arg; /* Nothing to work with */ if (!sc->sc_hdmi_framer) { device_printf(sc->sc_dev, "HDMI event without HDMI framer set\n"); return; } hdmi_dev = OF_device_from_xref(sc->sc_hdmi_framer); if (!hdmi_dev) { device_printf(sc->sc_dev, "no actual device for \"hdmi\" property\n"); return; } edid = NULL; edid_len = 0; if (HDMI_GET_EDID(hdmi_dev, &edid, &edid_len) != 0) { device_printf(sc->sc_dev, "failed to get EDID info from HDMI framer\n"); return; } videomode = NULL; if (edid_parse(edid, &ei) == 0) { edid_print(&ei); videomode = am335x_lcd_pick_mode(&ei); } else device_printf(sc->sc_dev, "failed to parse EDID\n"); /* Use standard VGA as fallback */ if (videomode == NULL) videomode = pick_mode_by_ref(640, 480, 60); if (videomode == NULL) { device_printf(sc->sc_dev, "failed to find usable videomode"); return; } device_printf(sc->sc_dev, "detected videomode: %dx%d @ %dKHz\n", videomode->hdisplay, videomode->vdisplay, am335x_mode_vrefresh(videomode)); sc->sc_panel.panel_width = videomode->hdisplay; sc->sc_panel.panel_height = videomode->vdisplay; sc->sc_panel.panel_hfp = videomode->hsync_start - videomode->hdisplay; sc->sc_panel.panel_hbp = videomode->htotal - videomode->hsync_end; sc->sc_panel.panel_hsw = videomode->hsync_end - videomode->hsync_start; sc->sc_panel.panel_vfp = videomode->vsync_start - videomode->vdisplay; sc->sc_panel.panel_vbp = videomode->vtotal - videomode->vsync_end; sc->sc_panel.panel_vsw = videomode->vsync_end - videomode->vsync_start; sc->sc_panel.pixelclk_active = 1; /* logic for HSYNC should be reversed */ if (videomode->flags & VID_NHSYNC) sc->sc_panel.hsync_active = 1; else sc->sc_panel.hsync_active = 0; if (videomode->flags & VID_NVSYNC) sc->sc_panel.vsync_active = 0; else sc->sc_panel.vsync_active = 1; sc->sc_panel.panel_pxl_clk = videomode->dot_clock * 1000; am335x_lcd_configure(sc); memcpy(&hdmi_mode, videomode, sizeof(hdmi_mode)); hdmi_mode.hskew = videomode->hsync_end - videomode->hsync_start; hdmi_mode.flags |= VID_HSKEW; HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); } static int am335x_lcd_probe(device_t dev) { #ifdef DEV_SC int err; #endif if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,am33xx-tilcdc")) return (ENXIO); device_set_desc(dev, "AM335x LCD controller"); #ifdef DEV_SC err = sc_probe_unit(device_get_unit(dev), device_get_flags(dev) | SC_AUTODETECT_KBD); if (err != 0) return (err); #endif return (BUS_PROBE_DEFAULT); } static int am335x_lcd_attach(device_t dev) { struct am335x_lcd_softc *sc; int err; int rid; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; phandle_t root, panel_node; err = 0; sc = device_get_softc(dev); sc->sc_dev = dev; am335x_read_hdmi_property(dev); root = OF_finddevice("/"); if (root == -1) { device_printf(dev, "failed to get FDT root node\n"); return (ENXIO); } + /* Fixme: Cant find any reference in DTS for dpll_disp_ck@498 for now. */ + err = clk_get_by_name(dev, "dpll_disp_ck@498", &sc->sc_clk_dpll_disp_ck); + if (err != 0) { + device_printf(dev, "Cant get dpll_disp_ck@49\n"); + return (ENXIO); + } + sc->sc_panel.ac_bias = 255; sc->sc_panel.ac_bias_intrpt = 0; sc->sc_panel.dma_burst_sz = 16; sc->sc_panel.bpp = 16; sc->sc_panel.fdd = 128; sc->sc_panel.sync_edge = 0; sc->sc_panel.sync_ctrl = 1; panel_node = fdt_find_compatible(root, "ti,tilcdc,panel", 1); if (panel_node != 0) { device_printf(dev, "using static panel info\n"); if (am335x_read_panel_info(dev, panel_node, &sc->sc_panel)) { device_printf(dev, "failed to read panel info\n"); return (ENXIO); } if (am335x_read_timing(dev, panel_node, &sc->sc_panel)) { device_printf(dev, "failed to read timings\n"); return (ENXIO); } } - ti_prcm_clk_enable(LCDC_CLK); + err = ti_sysc_clock_enable(device_get_parent(dev)); + if (err != 0) { + device_printf(dev, "Failed to enable sysc clkctrl, err %d\n", err); + return (ENXIO); + } rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, am335x_lcd_intr, sc, &sc->sc_intr_hl) != 0) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem_res); device_printf(dev, "Unable to setup the irq handler.\n"); return (ENXIO); } LCD_LOCK_INIT(sc); /* Init backlight interface */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree = device_get_sysctl_tree(sc->sc_dev); sc->sc_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "backlight", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, am335x_lcd_sysctl_backlight, "I", "LCD backlight"); sc->sc_backlight = 0; /* Check if eCAS interface is available at this point */ if (am335x_pwm_config_ecap(PWM_UNIT, PWM_PERIOD, PWM_PERIOD) == 0) sc->sc_backlight = 100; if (panel_node != 0) am335x_lcd_configure(sc); else sc->sc_hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event, am335x_lcd_hdmi_event, sc, EVENTHANDLER_PRI_ANY); return (0); } static int am335x_lcd_detach(device_t dev) { /* Do not let unload driver */ return (EBUSY); } static struct fb_info * am335x_lcd_fb_getinfo(device_t dev) { struct am335x_lcd_softc *sc; sc = device_get_softc(dev); return (&sc->sc_fb_info); } static device_method_t am335x_lcd_methods[] = { DEVMETHOD(device_probe, am335x_lcd_probe), DEVMETHOD(device_attach, am335x_lcd_attach), DEVMETHOD(device_detach, am335x_lcd_detach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, am335x_lcd_fb_getinfo), DEVMETHOD_END }; static driver_t am335x_lcd_driver = { "fb", am335x_lcd_methods, sizeof(struct am335x_lcd_softc), }; static devclass_t am335x_lcd_devclass; DRIVER_MODULE(am335x_lcd, simplebus, am335x_lcd_driver, am335x_lcd_devclass, 0, 0); MODULE_VERSION(am335x_lcd, 1); MODULE_DEPEND(am335x_lcd, simplebus, 1, 1, 1); +MODULE_DEPEND(am335x_lcd, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_musb.c =================================================================== --- head/sys/arm/ti/am335x/am335x_musb.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_musb.c (revision 363700) @@ -1,421 +1,462 @@ /*- * Copyright (c) 2013 Oleksandr Tymoshenko * * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usbssdebug #include #include #include #include #include -#include -#include #include +#include +#include +#include +#include "syscon_if.h" #define USBCTRL_REV 0x00 #define USBCTRL_CTRL 0x14 #define USBCTRL_STAT 0x18 #define USBCTRL_IRQ_STAT0 0x30 #define IRQ_STAT0_RXSHIFT 16 #define IRQ_STAT0_TXSHIFT 0 #define USBCTRL_IRQ_STAT1 0x34 #define IRQ_STAT1_DRVVBUS (1 << 8) #define USBCTRL_INTEN_SET0 0x38 #define USBCTRL_INTEN_SET1 0x3C #define USBCTRL_INTEN_USB_ALL 0x1ff #define USBCTRL_INTEN_USB_SOF (1 << 3) #define USBCTRL_INTEN_CLR0 0x40 #define USBCTRL_INTEN_CLR1 0x44 #define USBCTRL_UTMI 0xE0 #define USBCTRL_UTMI_FSDATAEXT (1 << 1) #define USBCTRL_MODE 0xE8 #define USBCTRL_MODE_IDDIG (1 << 8) #define USBCTRL_MODE_IDDIGMUX (1 << 7) /* USBSS resource + 2 MUSB ports */ #define RES_USBCORE 0 #define RES_USBCTRL 1 #define USB_WRITE4(sc, idx, reg, val) do { \ bus_write_4((sc)->sc_mem_res[idx], (reg), (val)); \ } while (0) #define USB_READ4(sc, idx, reg) bus_read_4((sc)->sc_mem_res[idx], (reg)) #define USBCTRL_WRITE4(sc, reg, val) \ USB_WRITE4((sc), RES_USBCTRL, (reg), (val)) #define USBCTRL_READ4(sc, reg) \ USB_READ4((sc), RES_USBCTRL, (reg)) static struct resource_spec am335x_musbotg_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { -1, 0, 0 } }; #ifdef USB_DEBUG static int usbssdebug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, am335x_usbss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "AM335x USBSS"); SYSCTL_INT(_hw_usb_am335x_usbss, OID_AUTO, debug, CTLFLAG_RW, &usbssdebug, 0, "Debug level"); #endif static device_probe_t musbotg_probe; static device_attach_t musbotg_attach; static device_detach_t musbotg_detach; struct musbotg_super_softc { struct musbotg_softc sc_otg; struct resource *sc_mem_res[2]; int sc_irq_rid; + struct syscon *syscon; }; static void musbotg_vbus_poll(struct musbotg_super_softc *sc) { uint32_t stat; if (sc->sc_otg.sc_mode == MUSB2_DEVICE_MODE) musbotg_vbus_interrupt(&sc->sc_otg, 1); else { stat = USBCTRL_READ4(sc, USBCTRL_STAT); musbotg_vbus_interrupt(&sc->sc_otg, stat & 1); } } /* * Arg to musbotg_clocks_on and musbot_clocks_off is * a uint32_t * pointing to the SCM register offset. */ static uint32_t USB_CTRL[] = {SCM_USB_CTRL0, SCM_USB_CTRL1}; static void musbotg_clocks_on(void *arg) { struct musbotg_softc *sc; - uint32_t c, reg; + struct musbotg_super_softc *ssc; + uint32_t reg; sc = arg; - reg = USB_CTRL[sc->sc_id]; + ssc = sc->sc_platform_data; - ti_scm_reg_read_4(reg, &c); - c &= ~3; /* Enable power */ - c |= 1 << 19; /* VBUS detect enable */ - c |= 1 << 20; /* Session end enable */ - ti_scm_reg_write_4(reg, c); + reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]); + reg &= ~3; /* Enable power */ + reg |= 1 << 19; /* VBUS detect enable */ + reg |= 1 << 20; /* Session end enable */ + + SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg); } static void musbotg_clocks_off(void *arg) { struct musbotg_softc *sc; - uint32_t c, reg; + struct musbotg_super_softc *ssc; + uint32_t reg; sc = arg; - reg = USB_CTRL[sc->sc_id]; + ssc = sc->sc_platform_data; /* Disable power to PHY */ - ti_scm_reg_read_4(reg, &c); - ti_scm_reg_write_4(reg, c | 3); + reg = SYSCON_READ_4(ssc->syscon, USB_CTRL[sc->sc_id]); + SYSCON_WRITE_4(ssc->syscon, USB_CTRL[sc->sc_id], reg | 3); } static void musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) { struct musbotg_super_softc *ssc = sc->sc_platform_data; uint32_t epmask; epmask = ((1 << ep) << IRQ_STAT0_RXSHIFT); epmask |= ((1 << ep) << IRQ_STAT0_TXSHIFT); if (on) USBCTRL_WRITE4(ssc, USBCTRL_INTEN_SET0, epmask); else USBCTRL_WRITE4(ssc, USBCTRL_INTEN_CLR0, epmask); } static void musbotg_wrapper_interrupt(void *arg) { struct musbotg_softc *sc = arg; struct musbotg_super_softc *ssc = sc->sc_platform_data; uint32_t stat, stat0, stat1; stat = USBCTRL_READ4(ssc, USBCTRL_STAT); stat0 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT0); stat1 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT1); if (stat0) USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT0, stat0); if (stat1) USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT1, stat1); DPRINTFN(4, "port%d: stat0=%08x stat1=%08x, stat=%08x\n", sc->sc_id, stat0, stat1, stat); if (stat1 & IRQ_STAT1_DRVVBUS) musbotg_vbus_interrupt(sc, stat & 1); musbotg_interrupt(arg, ((stat0 >> 16) & 0xffff), stat0 & 0xffff, stat1 & 0xff); } static int musbotg_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,musb-am33xx")) return (ENXIO); device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); return (BUS_PROBE_DEFAULT); } static int musbotg_attach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); char mode[16]; int err; uint32_t reg; + phandle_t opp_table; + clk_t clk_usbotg_fck; sc->sc_otg.sc_id = device_get_unit(dev); + /* FIXME: The devicetree needs to be updated to get a handle to the gate + * usbotg_fck@47c. see TRM 8.1.12.2 CM_WKUP CM_CLKDCOLDO_DPLL_PER. + */ + err = clk_get_by_name(dev, "usbotg_fck@47c", &clk_usbotg_fck); + if (err) { + device_printf(dev, "Can not find usbotg_fck@47c\n"); + return (ENXIO); + } + + err = clk_enable(clk_usbotg_fck); + if (err) { + device_printf(dev, "Can not enable usbotg_fck@47c\n"); + return (ENXIO); + } + + /* FIXME: For now; Go and kidnap syscon from opp-table */ + opp_table = OF_finddevice("/opp-table"); + if (opp_table == -1) { + device_printf(dev, "Cant find /opp-table\n"); + return (ENXIO); + } + if (!OF_hasprop(opp_table, "syscon")) { + device_printf(dev, "/opp-table missing syscon property\n"); + return (ENXIO); + } + err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon); + if (err) { + device_printf(dev, "Failed to get syscon\n"); + return (ENXIO); + } + /* Request the memory resources */ err = bus_alloc_resources(dev, am335x_musbotg_mem_spec, sc->sc_mem_res); if (err) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources */ sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE); if (sc->sc_otg.sc_irq_res == NULL) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* setup MUSB OTG USB controller interface softc */ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on; sc->sc_otg.sc_clocks_off = &musbotg_clocks_off; sc->sc_otg.sc_clocks_arg = &sc->sc_otg; sc->sc_otg.sc_ep_int_set = musbotg_ep_int_set; /* initialise some bus fields */ sc->sc_otg.sc_bus.parent = dev; sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices; sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES; sc->sc_otg.sc_bus.dma_bits = 32; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { device_printf(dev, "Failed allocate bus mem for musb\n"); return (ENOMEM); } sc->sc_otg.sc_io_res = sc->sc_mem_res[RES_USBCORE]; sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_otg.sc_bus.bdev)) { device_printf(dev, "No busdev for musb\n"); goto error; } device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)musbotg_wrapper_interrupt, &sc->sc_otg, &sc->sc_otg.sc_intr_hdl); if (err) { sc->sc_otg.sc_intr_hdl = NULL; device_printf(dev, "Failed to setup interrupt for musb\n"); goto error; } sc->sc_otg.sc_platform_data = sc; if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", mode, sizeof(mode)) > 0) { if (strcasecmp(mode, "host") == 0) sc->sc_otg.sc_mode = MUSB2_HOST_MODE; else sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; } else { /* Beaglebone defaults: USB0 device, USB1 HOST. */ if (sc->sc_otg.sc_id == 0) sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; else sc->sc_otg.sc_mode = MUSB2_HOST_MODE; } /* * software-controlled function */ if (sc->sc_otg.sc_mode == MUSB2_HOST_MODE) { reg = USBCTRL_READ4(sc, USBCTRL_MODE); reg |= USBCTRL_MODE_IDDIGMUX; reg &= ~USBCTRL_MODE_IDDIG; USBCTRL_WRITE4(sc, USBCTRL_MODE, reg); USBCTRL_WRITE4(sc, USBCTRL_UTMI, USBCTRL_UTMI_FSDATAEXT); } else { reg = USBCTRL_READ4(sc, USBCTRL_MODE); reg |= USBCTRL_MODE_IDDIGMUX; reg |= USBCTRL_MODE_IDDIG; USBCTRL_WRITE4(sc, USBCTRL_MODE, reg); } reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF; USBCTRL_WRITE4(sc, USBCTRL_INTEN_SET1, reg); USBCTRL_WRITE4(sc, USBCTRL_INTEN_CLR0, 0xffffffff); err = musbotg_init(&sc->sc_otg); if (!err) err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); if (err) goto error; /* poll VBUS one time */ musbotg_vbus_poll(sc); return (0); error: musbotg_detach(dev); return (ENXIO); } static int musbotg_detach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); int err; /* during module unload there are lots of children leftover */ device_delete_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call musbotg_uninit() after musbotg_init() */ musbotg_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); /* Free resources if any */ if (sc->sc_mem_res[0]) bus_release_resources(dev, am335x_musbotg_mem_spec, sc->sc_mem_res); if (sc->sc_otg.sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_otg.sc_irq_res); return (0); } static device_method_t musbotg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, musbotg_probe), DEVMETHOD(device_attach, musbotg_attach), DEVMETHOD(device_detach, musbotg_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t musbotg_driver = { .name = "musbotg", .methods = musbotg_methods, .size = sizeof(struct musbotg_super_softc), }; static devclass_t musbotg_devclass; -DRIVER_MODULE(musbotg, usbss, musbotg_driver, musbotg_devclass, 0, 0); -MODULE_DEPEND(musbotg, usbss, 1, 1, 1); +DRIVER_MODULE(musbotg, ti_sysc, musbotg_driver, musbotg_devclass, 0, 0); +MODULE_DEPEND(musbotg, ti_sysc, 1, 1, 1); +MODULE_DEPEND(musbotg, ti_am3359_cppi41, 1, 1, 1); +MODULE_DEPEND(usbss, usb, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_pwmss.c =================================================================== --- head/sys/arm/ti/am335x/am335x_pwmss.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_pwmss.c (revision 363700) @@ -1,163 +1,179 @@ /*- * Copyright (c) 2013 Oleksandr Tymoshenko * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include +#include +#include +#include "syscon_if.h" + #include "am335x_pwm.h" #include "am335x_scm.h" #define PWMSS_IDVER 0x00 #define PWMSS_SYSCONFIG 0x04 #define PWMSS_CLKCONFIG 0x08 #define CLKCONFIG_EPWMCLK_EN (1 << 8) #define PWMSS_CLKSTATUS 0x0C +/* TRM chapter 2 memory map table 2-3 + VER register location */ +#define PWMSS_REV_0 0x0000 +#define PWMSS_REV_1 0x2000 +#define PWMSS_REV_2 0x4000 + static device_probe_t am335x_pwmss_probe; static device_attach_t am335x_pwmss_attach; static device_detach_t am335x_pwmss_detach; struct am335x_pwmss_softc { struct simplebus_softc sc_simplebus; device_t sc_dev; - clk_ident_t sc_clk; + struct syscon *syscon; }; static device_method_t am335x_pwmss_methods[] = { DEVMETHOD(device_probe, am335x_pwmss_probe), DEVMETHOD(device_attach, am335x_pwmss_attach), DEVMETHOD(device_detach, am335x_pwmss_detach), DEVMETHOD_END }; static int am335x_pwmss_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,am33xx-pwmss")) return (ENXIO); device_set_desc(dev, "AM335x PWM"); return (BUS_PROBE_DEFAULT); } static int am335x_pwmss_attach(device_t dev) { struct am335x_pwmss_softc *sc; uint32_t reg, id; - phandle_t node; + uint64_t rev_address; + phandle_t node, opp_table; sc = device_get_softc(dev); sc->sc_dev = dev; - sc->sc_clk = ti_hwmods_get_clock(dev); - if (sc->sc_clk == INVALID_CLK_IDENT) { - device_printf(dev, "failed to get device id based on ti,hwmods\n"); - return (EINVAL); + /* FIXME: For now; Go and kidnap syscon from opp-table */ + opp_table = OF_finddevice("/opp-table"); + if (opp_table == -1) { + device_printf(dev, "Cant find /opp-table\n"); + return (ENXIO); } + if (!OF_hasprop(opp_table, "syscon")) { + device_printf(dev, "/opp-table doesnt have required syscon property\n"); + return (ENXIO); + } + if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon) != 0) { + device_printf(dev, "Failed to get syscon\n"); + return (ENXIO); + } - ti_prcm_clk_enable(sc->sc_clk); - ti_scm_reg_read_4(SCM_PWMSS_CTRL, ®); - switch (sc->sc_clk) { - case PWMSS0_CLK: - id = 0; - break; - case PWMSS1_CLK: - id = 1; - break; + ti_sysc_clock_enable(device_get_parent(dev)); - case PWMSS2_CLK: - id = 2; - break; - default: - device_printf(dev, "unknown pwmss clock id: %d\n", sc->sc_clk); - return (EINVAL); + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + switch (rev_address) { + case PWMSS_REV_0: + id = 0; + break; + case PWMSS_REV_1: + id = 1; + break; + case PWMSS_REV_2: + id = 2; + break; } + + reg = SYSCON_READ_4(sc->syscon, SCM_PWMSS_CTRL); reg |= (1 << id); - ti_scm_reg_write_4(SCM_PWMSS_CTRL, reg); + SYSCON_WRITE_4(sc->syscon, SCM_PWMSS_CTRL, reg); node = ofw_bus_get_node(dev); if (node == -1) return (ENXIO); simplebus_init(dev, node); /* * Allow devices to identify. */ bus_generic_probe(dev); /* * Now walk the OFW tree and attach top-level devices. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) simplebus_add_device(dev, node, 0, NULL, -1, NULL); return (bus_generic_attach(dev)); } static int am335x_pwmss_detach(device_t dev) { return (0); } DEFINE_CLASS_1(am335x_pwmss, am335x_pwmss_driver, am335x_pwmss_methods, sizeof(struct am335x_pwmss_softc), simplebus_driver); static devclass_t am335x_pwmss_devclass; DRIVER_MODULE(am335x_pwmss, simplebus, am335x_pwmss_driver, am335x_pwmss_devclass, 0, 0); MODULE_VERSION(am335x_pwmss, 1); +MODULE_DEPEND(am335x_pwmss, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_rtc.c =================================================================== --- head/sys/arm/ti/am335x/am335x_rtc.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_rtc.c (revision 363700) @@ -1,211 +1,213 @@ /*- * Copyright (c) 2015 Luiz Otavio O Souza * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include +#include #include -#include +#include #include #include #define RTC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define RTC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define RTC_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "am335x_rtc", MTX_DEF) #define RTC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define RTC_READ4(_sc, reg) \ bus_read_4((_sc)->sc_mem_res, reg) #define RTC_WRITE4(_sc, reg, value) \ bus_write_4((_sc)->sc_mem_res, reg, value) #define RTC_MAXIRQS 2 struct am335x_rtc_softc { device_t sc_dev; struct mtx sc_mtx; struct resource *sc_irq_res[RTC_MAXIRQS]; struct resource *sc_mem_res; }; static struct am335x_rtc_softc *rtc_sc = NULL; static struct resource_spec am335x_rtc_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0, 0 } }; static int am335x_rtc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,da830-rtc")) return (ENXIO); device_set_desc(dev, "AM335x RTC (power management mode)"); return (BUS_PROBE_DEFAULT); } static int am335x_rtc_attach(device_t dev) { int rid; struct am335x_rtc_softc *sc; uint32_t rev; if (rtc_sc != NULL) return (ENXIO); rtc_sc = sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory resources\n"); return (ENXIO); } if (bus_alloc_resources(dev, am335x_rtc_irq_spec, sc->sc_irq_res) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate irq resources\n"); return (ENXIO); } RTC_LOCK_INIT(sc); /* Enable the RTC module. */ - ti_prcm_clk_enable(RTC_CLK); + ti_sysc_clock_enable(device_get_parent(dev)); rev = RTC_READ4(sc, RTC_REVISION); device_printf(dev, "AM335X RTC v%d.%d.%d\n", (rev >> 8) & 0x7, (rev >> 6) & 0x3, rev & 0x3f); /* Unlock the RTC. */ RTC_WRITE4(sc, RTC_KICK0R, RTC_KICK0R_PASS); RTC_WRITE4(sc, RTC_KICK1R, RTC_KICK1R_PASS); /* Stop the RTC, we don't need it right now. */ RTC_WRITE4(sc, RTC_CTRL, 0); /* Disable interrupts. */ RTC_WRITE4(sc, RTC_INTR, 0); /* Ack any pending interrupt. */ RTC_WRITE4(sc, RTC_STATUS, RTC_STATUS_ALARM2 | RTC_STATUS_ALARM); /* Enable external clock (xtal) and 32 kHz clock. */ RTC_WRITE4(sc, RTC_OSC, RTC_OSC_32KCLK_EN | RTC_OSC_32KCLK_SEL); /* Enable pmic_pwr_enable. */ RTC_WRITE4(sc, RTC_PMIC, PMIC_PWR_ENABLE); return (0); } static int am335x_rtc_detach(device_t dev) { struct am335x_rtc_softc *sc; sc = device_get_softc(dev); if (sc->sc_irq_res[0] != NULL) bus_release_resources(dev, am335x_rtc_irq_spec, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); RTC_LOCK_DESTROY(sc); return (0); } void am335x_rtc_pmic_pwr_toggle(void) { int timeout; struct clocktime ct; struct timespec ts; /* * We stop the RTC so we don't need to check the STATUS.BUSY bit * before update ALARM2 registers. */ timeout = 10; RTC_WRITE4(rtc_sc, RTC_CTRL, 0); while (--timeout && RTC_READ4(rtc_sc, RTC_STATUS) & RTC_STATUS_RUN) DELAY(100); if (timeout == 0) { device_printf(rtc_sc->sc_dev, "RTC does not stop.\n"); return; } /* Program the ALARM2 to fire in 2 seconds. */ ct.dow = 0; ct.nsec = 0; ct.sec = FROMBCD(RTC_READ4(rtc_sc, RTC_SECONDS) & 0x7f); ct.min = FROMBCD(RTC_READ4(rtc_sc, RTC_MINUTES) & 0x7f); ct.hour = FROMBCD(RTC_READ4(rtc_sc, RTC_HOURS) & 0x3f); ct.day = FROMBCD(RTC_READ4(rtc_sc, RTC_DAYS) & 0x3f); ct.mon = FROMBCD(RTC_READ4(rtc_sc, RTC_MONTHS) & 0x1f); ct.year = FROMBCD(RTC_READ4(rtc_sc, RTC_YEARS) & 0xff); ct.year += POSIX_BASE_YEAR; clock_ct_to_ts(&ct, &ts); ts.tv_sec += 2; clock_ts_to_ct(&ts, &ct); RTC_WRITE4(rtc_sc, RTC_ALARM2_SECONDS, TOBCD(ct.sec)); RTC_WRITE4(rtc_sc, RTC_ALARM2_MINUTES, TOBCD(ct.min)); RTC_WRITE4(rtc_sc, RTC_ALARM2_HOURS, TOBCD(ct.hour)); RTC_WRITE4(rtc_sc, RTC_ALARM2_DAYS, TOBCD(ct.day)); RTC_WRITE4(rtc_sc, RTC_ALARM2_MONTHS, TOBCD(ct.mon)); RTC_WRITE4(rtc_sc, RTC_ALARM2_YEARS, TOBCD(ct.year - POSIX_BASE_YEAR)); /* Enable ALARM2 interrupt. */ RTC_WRITE4(rtc_sc, RTC_INTR, RTC_INTR_ALARM2); /* Start count. */ RTC_WRITE4(rtc_sc, RTC_CTRL, RTC_CTRL_RUN); } static device_method_t am335x_rtc_methods[] = { DEVMETHOD(device_probe, am335x_rtc_probe), DEVMETHOD(device_attach, am335x_rtc_attach), DEVMETHOD(device_detach, am335x_rtc_detach), DEVMETHOD_END }; static driver_t am335x_rtc_driver = { "am335x_rtc", am335x_rtc_methods, sizeof(struct am335x_rtc_softc), }; static devclass_t am335x_rtc_devclass; DRIVER_MODULE(am335x_rtc, simplebus, am335x_rtc_driver, am335x_rtc_devclass, 0, 0); MODULE_VERSION(am335x_rtc, 1); MODULE_DEPEND(am335x_rtc, simplebus, 1, 1, 1); +MODULE_DEPEND(am335x_rtc, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_scm.c =================================================================== --- head/sys/arm/ti/am335x/am335x_scm.c (revision 363699) +++ head/sys/arm/ti/am335x/am335x_scm.c (revision 363700) @@ -1,172 +1,198 @@ /*- * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include +#include +#include "syscon_if.h" + #define TZ_ZEROC 2731 struct am335x_scm_softc { int sc_last_temp; struct sysctl_oid *sc_temp_oid; + struct syscon *syscon; }; static int am335x_scm_temp_sysctl(SYSCTL_HANDLER_ARGS) { device_t dev; int i, temp; struct am335x_scm_softc *sc; uint32_t reg; dev = (device_t)arg1; sc = device_get_softc(dev); /* Read the temperature and convert to Kelvin. */ for(i = 50; i > 0; i--) { - ti_scm_reg_read_4(SCM_BGAP_CTRL, ®); + reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); if ((reg & SCM_BGAP_EOCZ) == 0) break; DELAY(50); } if ((reg & SCM_BGAP_EOCZ) == 0) { sc->sc_last_temp = (reg >> SCM_BGAP_TEMP_SHIFT) & SCM_BGAP_TEMP_MASK; sc->sc_last_temp *= 10; } temp = sc->sc_last_temp + TZ_ZEROC; return (sysctl_handle_int(oidp, &temp, 0, req)); } static void am335x_scm_identify(driver_t *driver, device_t parent) { device_t child; /* AM335x only. */ if (ti_chip() != CHIP_AM335X) return; /* Make sure we attach only once. */ if (device_find_child(parent, "am335x_scm", -1) != NULL) return; child = device_add_child(parent, "am335x_scm", -1); if (child == NULL) device_printf(parent, "cannot add ti_scm child\n"); } static int am335x_scm_probe(device_t dev) { + /* Just allow the first one */ + if (strcmp(device_get_nameunit(dev), "am335x_scm0") != 0) + return (ENXIO); device_set_desc(dev, "AM335x Control Module Extension"); return (BUS_PROBE_DEFAULT); } static int am335x_scm_attach(device_t dev) { struct am335x_scm_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; uint32_t reg; + phandle_t opp_table; + int err; + sc = device_get_softc(dev); + + /* FIXME: For now; Go and kidnap syscon from opp-table */ + opp_table = OF_finddevice("/opp-table"); + if (opp_table == -1) { + device_printf(dev, "Cant find /opp-table\n"); + return (ENXIO); + } + if (!OF_hasprop(opp_table, "syscon")) { + device_printf(dev, "/opp-table missing syscon property\n"); + return (ENXIO); + } + err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon); + if (err) { + device_printf(dev, "Failed to get syscon\n"); + return (ENXIO); + } + /* Reset the digital outputs. */ - ti_scm_reg_write_4(SCM_BGAP_CTRL, 0); - ti_scm_reg_read_4(SCM_BGAP_CTRL, ®); + SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, 0); + reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); DELAY(500); /* Set continous mode. */ - ti_scm_reg_write_4(SCM_BGAP_CTRL, SCM_BGAP_CONTCONV); - ti_scm_reg_read_4(SCM_BGAP_CTRL, ®); + SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_CONTCONV); + reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); DELAY(500); /* Start the ADC conversion. */ reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV | SCM_BGAP_SOC; - ti_scm_reg_write_4(SCM_BGAP_CTRL, reg); + SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, reg); /* Temperature sysctl. */ - sc = device_get_softc(dev); ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); sc->sc_temp_oid = SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0, am335x_scm_temp_sysctl, "IK", "Current temperature"); return (0); } static int am335x_scm_detach(device_t dev) { struct am335x_scm_softc *sc; sc = device_get_softc(dev); /* Remove temperature sysctl. */ if (sc->sc_temp_oid != NULL) sysctl_remove_oid(sc->sc_temp_oid, 1, 0); /* Stop the bandgap ADC. */ - ti_scm_reg_write_4(SCM_BGAP_CTRL, SCM_BGAP_BGOFF); + SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_BGOFF); return (0); } static device_method_t am335x_scm_methods[] = { DEVMETHOD(device_identify, am335x_scm_identify), DEVMETHOD(device_probe, am335x_scm_probe), DEVMETHOD(device_attach, am335x_scm_attach), DEVMETHOD(device_detach, am335x_scm_detach), DEVMETHOD_END }; static driver_t am335x_scm_driver = { "am335x_scm", am335x_scm_methods, sizeof(struct am335x_scm_softc), }; static devclass_t am335x_scm_devclass; DRIVER_MODULE(am335x_scm, ti_scm, am335x_scm_driver, am335x_scm_devclass, 0, 0); MODULE_VERSION(am335x_scm, 1); -MODULE_DEPEND(am335x_scm, ti_scm, 1, 1, 1); +MODULE_DEPEND(am335x_scm, ti_scm_syscon, 1, 1, 1); Index: head/sys/arm/ti/am335x/am335x_usb_phy.c =================================================================== --- head/sys/arm/ti/am335x/am335x_usb_phy.c (nonexistent) +++ head/sys/arm/ti/am335x/am335x_usb_phy.c (revision 363700) @@ -0,0 +1,121 @@ +/*- + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define TI_AM335X_USB_PHY 1 +#define TI_AM335X_USB_PHY_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,am335x-usb-phy", TI_AM335X_USB_PHY }, + { NULL, TI_AM335X_USB_PHY_END } +}; + +struct ti_usb_phy_softc { + device_t dev; +}; + +static int ti_usb_phy_probe(device_t dev); +static int ti_usb_phy_attach(device_t dev); +static int ti_usb_phy_detach(device_t dev); + +static int +ti_usb_phy_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI AM335x USB PHY"); + if (!bootverbose) + device_quiet(dev); + + return (BUS_PROBE_DEFAULT); +} + +static int +ti_usb_phy_attach(device_t dev) +{ + struct ti_usb_phy_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + /* FIXME: Add dev/extres/phy/ interface */ + + return (bus_generic_attach(dev)); +} + +static int +ti_usb_phy_detach(device_t dev) +{ + return (EBUSY); +} + +static device_method_t ti_usb_phy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_usb_phy_probe), + DEVMETHOD(device_attach, ti_usb_phy_attach), + DEVMETHOD(device_detach, ti_usb_phy_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ti_usb_phy, ti_usb_phy_driver, ti_usb_phy_methods, + sizeof(struct ti_usb_phy_softc), simplebus_driver); + +static devclass_t ti_usb_phy_devclass; + +EARLY_DRIVER_MODULE(ti_usb_phy, simplebus, ti_usb_phy_driver, + ti_usb_phy_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); +MODULE_VERSION(ti_usb_phy, 1); +MODULE_DEPEND(ti_usb_phy, ti_sysc, 1, 1, 1); Property changes on: head/sys/arm/ti/am335x/am335x_usb_phy.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/am335x/files.am335x =================================================================== --- head/sys/arm/ti/am335x/files.am335x (revision 363699) +++ head/sys/arm/ti/am335x/files.am335x (revision 363700) @@ -1,25 +1,25 @@ #$FreeBSD$ arm/ti/aintc.c standard arm/ti/am335x/am335x_dmtimer.c standard arm/ti/am335x/am335x_dmtpps.c optional am335x_dmtpps arm/ti/am335x/am335x_gpio.c optional gpio arm/ti/am335x/am335x_lcd.c optional sc | vt arm/ti/am335x/am335x_lcd_syscons.c optional sc arm/ti/am335x/am335x_pmic.c optional am335x_pmic -arm/ti/am335x/am335x_prcm.c standard arm/ti/am335x/am335x_pwmss.c standard dev/pwm/pwmbus_if.m standard arm/ti/am335x/am335x_ehrpwm.c standard arm/ti/am335x/am335x_ecap.c standard arm/ti/am335x/am335x_rtc.c optional am335x_rtc arm/ti/am335x/am335x_scm.c standard arm/ti/am335x/am335x_scm_padconf.c standard -arm/ti/am335x/am335x_usbss.c optional musb fdt arm/ti/am335x/am335x_musb.c optional musb fdt +arm/ti/am335x/am335x_usb_phy.c optional musb fdt +arm/ti/am335x/am3359_cppi41.c optional musb fdt arm/ti/am335x/tda19988.c optional hdmi arm/ti/ti_edma3.c standard arm/ti/cpsw/if_cpsw.c optional cpsw Index: head/sys/arm/ti/clk/clock_common.c =================================================================== --- head/sys/arm/ti/clk/clock_common.c (nonexistent) +++ head/sys/arm/ti/clk/clock_common.c (revision 363700) @@ -0,0 +1,152 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clock_common.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +void +read_clock_cells(device_t dev, struct clock_cell_info *clk) { + ssize_t numbytes_clocks; + phandle_t node, parent, *cells; + int index, ncells, rv; + + node = ofw_bus_get_node(dev); + + /* Get names of parent clocks */ + numbytes_clocks = OF_getproplen(node, "clocks"); + clk->num_clock_cells = numbytes_clocks / sizeof(cell_t); + + /* Allocate space and get clock cells content */ + /* clock_cells / clock_cells_ncells will be freed in + * find_parent_clock_names() + */ + clk->clock_cells = malloc(numbytes_clocks, M_DEVBUF, M_WAITOK|M_ZERO); + clk->clock_cells_ncells = malloc(clk->num_clock_cells*sizeof(uint8_t), + M_DEVBUF, M_WAITOK|M_ZERO); + OF_getencprop(node, "clocks", clk->clock_cells, numbytes_clocks); + + /* Count number of clocks */ + clk->num_real_clocks = 0; + for (index = 0; index < clk->num_clock_cells; index++) { + rv = ofw_bus_parse_xref_list_alloc(node, "clocks", "#clock-cells", + clk->num_real_clocks, &parent, &ncells, &cells); + if (rv != 0) + continue; + + if (cells != NULL) + OF_prop_free(cells); + + clk->clock_cells_ncells[index] = ncells; + index += ncells; + clk->num_real_clocks++; + } +} + +int +find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) { + int index, clock_index, err; + bool found_all = true; + clk_t parent; + + /* Figure out names */ + for (index = 0, clock_index = 0; index < clk->num_clock_cells; index++) { + /* Get name of parent clock */ + err = clk_get_by_ofw_index(dev, 0, clock_index, &parent); + if (err != 0) { + clock_index++; + found_all = false; + DPRINTF(dev, "Failed to find clock_cells[%d]=0x%x\n", + index, clk->clock_cells[index]); + + index += clk->clock_cells_ncells[index]; + continue; + } + + def->parent_names[clock_index] = clk_get_name(parent); + clk_release(parent); + + DPRINTF(dev, "Found parent clock[%d/%d]: %s\n", + clock_index, clk->num_real_clocks, + def->parent_names[clock_index]); + + clock_index++; + index += clk->clock_cells_ncells[index]; + } + + if (!found_all) { + return 1; + } + + free(clk->clock_cells, M_DEVBUF); + free(clk->clock_cells_ncells, M_DEVBUF); + return 0; +} + +void +create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def) { + def->id = 1; + + clk_parse_ofw_clk_name(dev, ofw_bus_get_node(dev), &def->name); + + DPRINTF(dev, "node name: %s\n", def->name); + + def->parent_cnt = clk->num_real_clocks; + def->parent_names = malloc(clk->num_real_clocks*sizeof(char *), + M_OFWPROP, M_WAITOK); +} +void +free_clkdef(struct clknode_init_def *def) { + OF_prop_free(__DECONST(char *, def->name)); + OF_prop_free(def->parent_names); +} Property changes on: head/sys/arm/ti/clk/clock_common.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/clock_common.h =================================================================== --- head/sys/arm/ti/clk/clock_common.h (nonexistent) +++ head/sys/arm/ti/clk/clock_common.h (revision 363700) @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +struct clock_cell_info { + cell_t *clock_cells; + uint8_t *clock_cells_ncells; + uint32_t num_clock_cells; + uint8_t num_real_clocks; +}; + +void read_clock_cells(device_t dev, struct clock_cell_info *clk); +int find_parent_clock_names(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def); +void create_clkdef(device_t dev, struct clock_cell_info *clk, struct clknode_init_def *def); +void free_clkdef(struct clknode_init_def *def); Property changes on: head/sys/arm/ti/clk/clock_common.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_clk_clkctrl.c =================================================================== --- head/sys/arm/ti/clk/ti_clk_clkctrl.c (nonexistent) +++ head/sys/arm/ti/clk/ti_clk_clkctrl.c (revision 363700) @@ -0,0 +1,219 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* + * clknode for clkctrl, implements gate and mux (for gpioc) + */ + +#define GPIO_X_GDBCLK_MASK 0x00040000 +#define IDLEST_MASK 0x00030000 +#define MODULEMODE_MASK 0x00000003 + +#define GPIOX_GDBCLK_ENABLE 0x00040000 +#define GPIOX_GDBCLK_DISABLE 0x00000000 +#define IDLEST_FUNC 0x00000000 +#define IDLEST_TRANS 0x00010000 +#define IDLEST_IDLE 0x00020000 +#define IDLEST_DISABLE 0x00030000 + +#define MODULEMODE_DISABLE 0x0 +#define MODULEMODE_ENABLE 0x2 + +struct ti_clkctrl_clknode_sc { + device_t dev; + bool gdbclk; + /* omap4-cm range.host + ti,clkctrl reg[0] */ + uint32_t register_offset; +}; + +#define WRITE4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define READ4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int +ti_clkctrl_init(struct clknode *clk, device_t dev) +{ + struct ti_clkctrl_clknode_sc *sc; + + sc = clknode_get_softc(clk); + sc->dev = dev; + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable) +{ + struct ti_clkctrl_clknode_sc *sc; + uint32_t val, gpio_x_gdbclk; + uint32_t timeout = 100; + + sc = clknode_get_softc(clk); + + READ4(clk, sc->register_offset, &val); + DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n", + val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK, + GPIO_X_GDBCLK_MASK | MODULEMODE_MASK); + + if (enable) { + val = val & MODULEMODE_MASK; + val |= GPIOX_GDBCLK_ENABLE; + } else { + val = val & MODULEMODE_MASK; + val |= GPIOX_GDBCLK_DISABLE; + } + + DPRINTF(sc->dev, "val %x\n", val); + WRITE4(clk, sc->register_offset, val); + + /* Wait */ + while (timeout) { + READ4(clk, sc->register_offset, &val); + gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK; + if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE)) + break; + else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE)) + break; + DELAY(10); + timeout--; + } + if (timeout == 0) { + device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n"); + return (1); + } + + return (0); +} + +static int +ti_clkctrl_set_gate(struct clknode *clk, bool enable) +{ + struct ti_clkctrl_clknode_sc *sc; + uint32_t val, idlest, module; + uint32_t timeout=100; + int err; + + sc = clknode_get_softc(clk); + + if (sc->gdbclk) { + err = ti_clkctrl_set_gdbclk_gate(clk, enable); + return (err); + } + + READ4(clk, sc->register_offset, &val); + + if (enable) + WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE); + else + WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE); + + while (timeout) { + READ4(clk, sc->register_offset, &val); + idlest = val & IDLEST_MASK; + module = val & MODULEMODE_MASK; + if (enable && + (idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) && + module == MODULEMODE_ENABLE) + break; + else if (!enable && + idlest == IDLEST_DISABLE && + module == MODULEMODE_DISABLE) + break; + DELAY(10); + timeout--; + } + + if (timeout == 0) { + device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n"); + return (1); + } + + return (0); +} + +static clknode_method_t ti_clkctrl_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, ti_clkctrl_init), + CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class, + ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc), + clknode_class); + +int +ti_clknode_clkctrl_register(struct clkdom *clkdom, + struct ti_clk_clkctrl_def *clkdef) +{ + struct clknode *clk; + struct ti_clkctrl_clknode_sc *sc; + + clk = clknode_create(clkdom, &ti_clkctrl_clknode_class, + &clkdef->clkdef); + + if (clk == NULL) { + return (1); + } + + sc = clknode_get_softc(clk); + sc->register_offset = clkdef->register_offset; + sc->gdbclk = clkdef->gdbclk; + + if (clknode_register(clkdom, clk) == NULL) { + return (2); + } + return (0); +} Property changes on: head/sys/arm/ti/clk/ti_clk_clkctrl.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_clk_clkctrl.h =================================================================== --- head/sys/arm/ti/clk/ti_clk_clkctrl.h (nonexistent) +++ head/sys/arm/ti/clk/ti_clk_clkctrl.h (revision 363700) @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_CLK_CLKCTRL_H_ +#define _TI_CLK_CLKCTRL_H_ + +#include + +struct ti_clk_clkctrl_def { + struct clknode_init_def clkdef; + bool gdbclk; + uint32_t register_offset; +}; + +int ti_clknode_clkctrl_register(struct clkdom *clkdom, struct ti_clk_clkctrl_def *clkdef); + +#endif /* _TI_CLK_CLKCTRL_H_ */ Property changes on: head/sys/arm/ti/clk/ti_clk_clkctrl.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_clk_dpll.c =================================================================== --- head/sys/arm/ti/clk/ti_clk_dpll.c (nonexistent) +++ head/sys/arm/ti/clk/ti_clk_dpll.c (revision 363700) @@ -0,0 +1,341 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Emmanuel Vadot + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * based on sys/arm/allwinner/clkng/aw_clk_np.c + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" + +/* + * clknode for clocks matching the formula : + * + * clk = clkin * n / p + * + */ + +struct ti_dpll_clknode_sc { + uint32_t ti_clkmode_offset; /* control */ + uint8_t ti_clkmode_flags; + + uint32_t ti_idlest_offset; + + uint32_t ti_clksel_offset; /* mult-div1 */ + struct ti_clk_factor n; /* ti_clksel_mult */ + struct ti_clk_factor p; /* ti_clksel_div */ + + uint32_t ti_autoidle_offset; +}; + +#define WRITE4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define READ4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int +ti_dpll_clk_init(struct clknode *clk, device_t dev) +{ + struct ti_dpll_clknode_sc *sc; + + sc = clknode_get_softc(clk); + + clknode_init_parent_idx(clk, 0); + return (0); +} + +/* helper to keep aw_clk_np_find_best "intact" */ +static inline uint32_t +ti_clk_factor_get_max(struct ti_clk_factor *factor) +{ + uint32_t max; + + if (factor->flags & TI_CLK_FACTOR_FIXED) + max = factor->value; + else { + max = (1 << factor->width); + } + + return (max); +} + +static inline uint32_t +ti_clk_factor_get_min(struct ti_clk_factor *factor) +{ + uint32_t min; + + if (factor->flags & TI_CLK_FACTOR_FIXED) + min = factor->value; + else if (factor->flags & TI_CLK_FACTOR_ZERO_BASED) + min = 0; + else if (factor->flags & TI_CLK_FACTOR_MIN_VALUE) + min = factor->min_value; + else + min = 1; + + return (min); +} + +static uint64_t +ti_dpll_clk_find_best(struct ti_dpll_clknode_sc *sc, uint64_t fparent, + uint64_t *fout, uint32_t *factor_n, uint32_t *factor_p) +{ + uint64_t cur, best; + uint32_t n, p, max_n, max_p, min_n, min_p; + + *factor_n = *factor_p = 0; + + max_n = ti_clk_factor_get_max(&sc->n); + max_p = ti_clk_factor_get_max(&sc->p); + min_n = ti_clk_factor_get_min(&sc->n); + min_p = ti_clk_factor_get_min(&sc->p); + + for (p = min_p; p <= max_p; ) { + for (n = min_n; n <= max_n; ) { + cur = fparent * n / p; + if (abs(*fout - cur) < abs(*fout - best)) { + best = cur; + *factor_n = n; + *factor_p = p; + } + + n++; + } + p++; + } + + return (best); +} + +static inline uint32_t +ti_clk_get_factor(uint32_t val, struct ti_clk_factor *factor) +{ + uint32_t factor_val; + + if (factor->flags & TI_CLK_FACTOR_FIXED) + return (factor->value); + + factor_val = (val & factor->mask) >> factor->shift; + if (!(factor->flags & TI_CLK_FACTOR_ZERO_BASED)) + factor_val += 1; + + return (factor_val); +} + +static inline uint32_t +ti_clk_factor_get_value(struct ti_clk_factor *factor, uint32_t raw) +{ + uint32_t val; + + if (factor->flags & TI_CLK_FACTOR_FIXED) + return (factor->value); + + if (factor->flags & TI_CLK_FACTOR_ZERO_BASED) + val = raw; + else if (factor->flags & TI_CLK_FACTOR_MAX_VALUE && + raw > factor->max_value) + val = factor->max_value; + else + val = raw - 1; + + return (val); +} + + +static int +ti_dpll_clk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct ti_dpll_clknode_sc *sc; + uint64_t cur, best; + uint32_t val, n, p, best_n, best_p, timeout; + + sc = clknode_get_softc(clk); + + best = cur = 0; + + best = ti_dpll_clk_find_best(sc, fparent, fout, + &best_n, &best_p); + + if ((flags & CLK_SET_DRYRUN) != 0) { + *fout = best; + *stop = 1; + return (0); + } + + if ((best < *fout) && + (flags == CLK_SET_ROUND_DOWN)) { + *stop = 1; + return (ERANGE); + } + if ((best > *fout) && + (flags == CLK_SET_ROUND_UP)) { + *stop = 1; + return (ERANGE); + } + + DEVICE_LOCK(clk); + /* 1 switch PLL to bypass mode */ + WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_MN_BYPASS_MODE); + + /* 2 Ensure PLL is in bypass */ + timeout = 10000; + do { + DELAY(10); + READ4(clk, sc->ti_idlest_offset, &val); + } while (!(val & ST_MN_BYPASS_MASK) && timeout--); + + if (timeout == 0) { + DEVICE_UNLOCK(clk); + return (ERANGE); // FIXME: Better return value? + } + + /* 3 Set DPLL_MULT & DPLL_DIV bits */ + READ4(clk, sc->ti_clksel_offset, &val); + + n = ti_clk_factor_get_value(&sc->n, best_n); + p = ti_clk_factor_get_value(&sc->p, best_p); + val &= ~sc->n.mask; + val &= ~sc->p.mask; + val |= n << sc->n.shift; + val |= p << sc->p.shift; + + WRITE4(clk, sc->ti_clksel_offset, val); + + /* 4. configure M2, M4, M5 and M6 */ + /* + * FIXME: According to documentation M2/M4/M5/M6 can be set "later" + * See note in TRM 8.1.6.7.1 + */ + + /* 5 Switch over to lock mode */ + WRITE4(clk, sc->ti_clkmode_offset, DPLL_EN_LOCK_MODE); + + /* 6 Ensure PLL is locked */ + timeout = 10000; + do { + DELAY(10); + READ4(clk, sc->ti_idlest_offset, &val); + } while (!(val & ST_DPLL_CLK_MASK) && timeout--); + + DEVICE_UNLOCK(clk); + if (timeout == 0) { + return (ERANGE); // FIXME: Better return value? + } + + *fout = best; + *stop = 1; + + return (0); +} + +static int +ti_dpll_clk_recalc(struct clknode *clk, uint64_t *freq) +{ + struct ti_dpll_clknode_sc *sc; + uint32_t val, n, p; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->ti_clksel_offset, &val); + DEVICE_UNLOCK(clk); + + n = ti_clk_get_factor(val, &sc->n); + p = ti_clk_get_factor(val, &sc->p); + + *freq = *freq * n / p; + + return (0); +} + +static clknode_method_t ti_dpll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, ti_dpll_clk_init), + CLKNODEMETHOD(clknode_recalc_freq, ti_dpll_clk_recalc), + CLKNODEMETHOD(clknode_set_freq, ti_dpll_clk_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(ti_dpll_clknode, ti_dpll_clknode_class, ti_dpll_clknode_methods, + sizeof(struct ti_dpll_clknode_sc), clknode_class); + +int +ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef) +{ + struct clknode *clk; + struct ti_dpll_clknode_sc *sc; + + clk = clknode_create(clkdom, &ti_dpll_clknode_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->ti_clkmode_offset = clkdef->ti_clkmode_offset; + sc->ti_clkmode_flags = clkdef->ti_clkmode_flags; + sc->ti_idlest_offset = clkdef->ti_idlest_offset; + sc->ti_clksel_offset = clkdef->ti_clksel_offset; + + sc->n.shift = clkdef->ti_clksel_mult.shift; + sc->n.mask = clkdef->ti_clksel_mult.mask; + sc->n.width = clkdef->ti_clksel_mult.width; + sc->n.value = clkdef->ti_clksel_mult.value; + sc->n.min_value = clkdef->ti_clksel_mult.min_value; + sc->n.max_value = clkdef->ti_clksel_mult.max_value; + sc->n.flags = clkdef->ti_clksel_mult.flags; + + sc->p.shift = clkdef->ti_clksel_div.shift; + sc->p.mask = clkdef->ti_clksel_div.mask; + sc->p.width = clkdef->ti_clksel_div.width; + sc->p.value = clkdef->ti_clksel_div.value; + sc->p.min_value = clkdef->ti_clksel_div.min_value; + sc->p.max_value = clkdef->ti_clksel_div.max_value; + sc->p.flags = clkdef->ti_clksel_div.flags; + + sc->ti_autoidle_offset = clkdef->ti_autoidle_offset; + + clknode_register(clkdom, clk); + + return (0); +} Property changes on: head/sys/arm/ti/clk/ti_clk_dpll.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_clk_dpll.h =================================================================== --- head/sys/arm/ti/clk/ti_clk_dpll.h (nonexistent) +++ head/sys/arm/ti/clk/ti_clk_dpll.h (revision 363700) @@ -0,0 +1,97 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2017 Emmanuel Vadot + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _TI_DPLL_CLOCK_H_ +#define _TI_DPLL_CLOCK_H_ + +#include + +/* Registers are described in AM335x TRM chapter 8.1.12.2.* */ + +/* Register offsets */ +#define CM_CLKSEL_DPLL_PERIPH 0x49C + +/* CM_IDLEST_DPLL_xxx */ +#define ST_MN_BYPASS_MASK 0x0100 +#define ST_MN_BYPASS_SHIFT 8 +#define ST_DPLL_CLK_MASK 0x0001 + +/* CM_CLKMODE_DPLL_DPLL_EN feature flag */ +#define LOW_POWER_STOP_MODE_FLAG 0x01 +#define MN_BYPASS_MODE_FLAG 0x02 +#define IDLE_BYPASS_LOW_POWER_MODE_FLAG 0x04 +#define IDLE_BYPASS_FAST_RELOCK_MODE_FLAG 0x08 +#define LOCK_MODE_FLAG 0x10 + +/* CM_CLKMODE_DPLL_xxx */ +#define DPLL_EN_LOW_POWER_STOP_MODE 0x01 +#define DPLL_EN_MN_BYPASS_MODE 0x04 +#define DPLL_EN_IDLE_BYPASS_LOW_POWER_MODE 0x05 +#define DPLL_EN_IDLE_BYPASS_FAST_RELOCK_MODE 0x06 +#define DPLL_EN_LOCK_MODE 0x07 + + +#define TI_CLK_FACTOR_ZERO_BASED 0x0002 +#define TI_CLK_FACTOR_FIXED 0x0008 +#define TI_CLK_FACTOR_MIN_VALUE 0x0020 +#define TI_CLK_FACTOR_MAX_VALUE 0x0040 + +/* Based on aw_clk_factor sys/arm/allwinner/clkng/aw_clk.h */ +struct ti_clk_factor { + uint32_t shift; /* Shift bits for the factor */ + uint32_t mask; /* Mask to get the factor */ + uint32_t width; /* Number of bits for the factor */ + uint32_t value; /* Fixed value */ + + uint32_t min_value; + uint32_t max_value; + + uint32_t flags; /* Flags */ +}; + +struct ti_clk_dpll_def { + struct clknode_init_def clkdef; + + uint32_t ti_clkmode_offset; /* control */ + uint8_t ti_clkmode_flags; + + uint32_t ti_idlest_offset; + + uint32_t ti_clksel_offset; /* mult-div1 */ + struct ti_clk_factor ti_clksel_mult; + struct ti_clk_factor ti_clksel_div; + + uint32_t ti_autoidle_offset; +}; + +int ti_clknode_dpll_register(struct clkdom *clkdom, struct ti_clk_dpll_def *clkdef); + +#endif /* _TI_DPLL_CLOCK_H_ */ Property changes on: head/sys/arm/ti/clk/ti_clk_dpll.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_clkctrl.c =================================================================== --- head/sys/arm/ti/clk/ti_clkctrl.c (nonexistent) +++ head/sys/arm/ti/clk/ti_clkctrl.c (revision 363700) @@ -0,0 +1,353 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2016 Michal Meloun + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +#define L4LS_CLKCTRL_38 2 +#define L4_WKUP_CLKCTRL_0 1 +#define NO_SPECIAL_REG 0 + +/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */ + +#define TI_CLKCTRL_L4_WKUP 5 +#define TI_CLKCTRL_L4_SECURE 4 +#define TI_CLKCTRL_L4_PER 3 +#define TI_CLKCTRL_L4_CFG 2 +#define TI_CLKCTRL 1 +#define TI_CLKCTRL_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,clkctrl-l4-wkup", TI_CLKCTRL_L4_WKUP }, + { "ti,clkctrl-l4-secure", TI_CLKCTRL_L4_SECURE }, + { "ti,clkctrl-l4-per", TI_CLKCTRL_L4_PER }, + { "ti,clkctrl-l4-cfg", TI_CLKCTRL_L4_CFG }, + { "ti,clkctrl", TI_CLKCTRL }, + { NULL, TI_CLKCTRL_END } +}; + +struct ti_clkctrl_softc { + device_t dev; + + struct clkdom *clkdom; +}; + +static int ti_clkctrl_probe(device_t dev); +static int ti_clkctrl_attach(device_t dev); +static int ti_clkctrl_detach(device_t dev); +int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk); +static int +create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset, + uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg); + +static int +ti_clkctrl_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI clkctrl"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ti_clkctrl_attach(device_t dev) +{ + struct ti_clkctrl_softc *sc; + phandle_t node; + cell_t *reg; + ssize_t numbytes_reg; + int num_reg, err, ti_clock_cells; + uint32_t index, reg_offset, reg_address; + const char *org_name; + uint64_t parent_offset; + uint8_t special_reg = NO_SPECIAL_REG; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + /* Sanity check */ + err = OF_searchencprop(node, "#clock-cells", + &ti_clock_cells, sizeof(ti_clock_cells)); + if (err == -1) { + device_printf(sc->dev, "Failed to get #clock-cells\n"); + return (ENXIO); + } + + if (ti_clock_cells != 2) { + device_printf(sc->dev, "clock cells(%d) != 2\n", + ti_clock_cells); + return (ENXIO); + } + + /* Grab the content of reg properties */ + numbytes_reg = OF_getproplen(node, "reg"); + if (numbytes_reg == 0) { + device_printf(sc->dev, "reg property empty - check your devicetree\n"); + return (ENXIO); + } + num_reg = numbytes_reg / sizeof(cell_t); + + reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "reg", reg, numbytes_reg); + + /* Create clock domain */ + sc->clkdom = clkdom_create(sc->dev); + if (sc->clkdom == NULL) { + free(reg, M_DEVBUF); + DPRINTF(sc->dev, "Failed to create clkdom\n"); + return (ENXIO); + } + clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map); + + /* Create clock nodes */ + /* name */ + clk_parse_ofw_clk_name(sc->dev, node, &org_name); + + /* Get parent range */ + parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev)); + + /* Check if this is a clkctrl with special registers like gpio */ + switch (ti_chip()) { +#ifdef SOC_OMAP4 + case CHIP_OMAP_4: + /* FIXME: Todo */ + break; + +#endif /* SOC_OMAP4 */ +#ifdef SOC_TI_AM335X + /* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3 + * and the DTS. + */ + case CHIP_AM335X: + if (strcmp(org_name, "l4ls-clkctrl@38") == 0) + special_reg = L4LS_CLKCTRL_38; + else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0) + special_reg = L4_WKUP_CLKCTRL_0; + break; +#endif /* SOC_TI_AM335X */ + default: + break; + } + + /* reg property has a pair of (base address, length) */ + for (index = 0; index < num_reg; index += 2) { + for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) { + + err = create_clkctrl(sc, reg, index, reg_offset, parent_offset, + org_name, false); + if (err) + goto cleanup; + + /* Create special clkctrl for GDBCLK in GPIO registers */ + switch (special_reg) { + case NO_SPECIAL_REG: + break; + case L4LS_CLKCTRL_38: + reg_address = reg[index] + reg_offset-reg[0]; + if (reg_address == 0x74 || + reg_address == 0x78 || + reg_address == 0x7C) + { + err = create_clkctrl(sc, reg, index, reg_offset, + parent_offset, org_name, true); + if (err) + goto cleanup; + } + break; + case L4_WKUP_CLKCTRL_0: + reg_address = reg[index] + reg_offset - reg[0]; + if (reg_address == 0x8) + { + err = create_clkctrl(sc, reg, index, reg_offset, + parent_offset, org_name, true); + if (err) + goto cleanup; + } + break; + } /* switch (special_reg) */ + } /* inner for */ + } /* for */ + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err); + err = ENXIO; + goto cleanup; + } + +cleanup: + OF_prop_free(__DECONST(char *, org_name)); + + free(reg, M_DEVBUF); + + if (err) + return (err); + + return (bus_generic_attach(dev)); +} + +static int +ti_clkctrl_detach(device_t dev) +{ + return (EBUSY); +} + +/* modified version of default mapper from clk.c */ +int +clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) { + if (ncells == 0) + *clk = clknode_find_by_id(clkdom, 1); + else if (ncells == 1) + *clk = clknode_find_by_id(clkdom, cells[0]); + else if (ncells == 2) { + /* To avoid collision with other IDs just add one. + * All other registers has an offset of 4 from each other. + */ + if (cells[1]) + *clk = clknode_find_by_id(clkdom, cells[0]+1); + else + *clk = clknode_find_by_id(clkdom, cells[0]); + } + else + return (ERANGE); + + if (*clk == NULL) + return (ENXIO); + + return (0); +} + +static int +create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset, + uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) { + struct ti_clk_clkctrl_def def; + char *name; + size_t name_len; + int err; + + name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */ + name = malloc(name_len, M_OFWPROP, M_WAITOK); + + /* + * Check out XX_CLKCTRL-INDEX(offset)-macro dance in + * sys/gnu/dts/dts/include/dt-bindings/clock/am3.h + * sys/gnu/dts/dts/include/dt-bindings/clock/am4.h + * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h + * reg[0] are in practice the same as the offset described in the dts. + */ + /* special_gdbclk_reg are 0 or 1 */ + def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg; + def.register_offset = parent_offset + reg[index] + reg_offset; + + /* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */ + def.gdbclk = special_gdbclk_reg; + + /* Make up an uniq name in the namespace for each clkctrl */ + snprintf(name, name_len, "%s_%x", + org_name, def.clkdef.id); + def.clkdef.name = (const char *) name; + + DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n", + index, def.clkdef.name, def.clkdef.id); + + /* No parent name */ + def.clkdef.parent_cnt = 0; + + /* set flags */ + def.clkdef.flags = 0x0; + + /* Register the clkctrl */ + err = ti_clknode_clkctrl_register(sc->clkdom, &def); + if (err) { + DPRINTF(sc->dev, + "ti_clknode_clkctrl_register[%d:%d] failed %x\n", + index, reg_offset, err); + err = ENXIO; + } + OF_prop_free(name); + return (err); +} + +static device_method_t ti_clkctrl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_clkctrl_probe), + DEVMETHOD(device_attach, ti_clkctrl_attach), + DEVMETHOD(device_detach, ti_clkctrl_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods, + sizeof(struct ti_clkctrl_softc)); + +static devclass_t ti_clkctrl_devclass; + +EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver, +ti_clkctrl_devclass, 0, 0, BUS_PASS_BUS+BUS_PASS_ORDER_MIDDLE); + +MODULE_VERSION(ti_clkctrl, 1); Property changes on: head/sys/arm/ti/clk/ti_clkctrl.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_divider_clock.c =================================================================== --- head/sys/arm/ti/clk/ti_divider_clock.c (nonexistent) +++ head/sys/arm/ti/clk/ti_divider_clock.c (revision 363700) @@ -0,0 +1,264 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clock_common.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* + * Devicetree description + * Documentation/devicetree/bindings/clock/ti/divider.txt + */ + +struct ti_divider_softc { + device_t sc_dev; + bool attach_done; + struct clk_div_def div_def; + + struct clock_cell_info clock_cell; + struct clkdom *clkdom; +}; + +static int ti_divider_probe(device_t dev); +static int ti_divider_attach(device_t dev); +static int ti_divider_detach(device_t dev); + +#define TI_DIVIDER_CLOCK 2 +#define TI_COMPOSITE_DIVIDER_CLOCK 1 +#define TI_DIVIDER_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,divider-clock", TI_DIVIDER_CLOCK }, + { "ti,composite-divider-clock", TI_COMPOSITE_DIVIDER_CLOCK }, + { NULL, TI_DIVIDER_END } +}; + +static int +register_clk(struct ti_divider_softc *sc) { + int err; + + sc->clkdom = clkdom_create(sc->sc_dev); + if (sc->clkdom == NULL) { + DPRINTF(sc->sc_dev, "Failed to create clkdom\n"); + return (ENXIO); + } + + err = clknode_div_register(sc->clkdom, &sc->div_def); + if (err) { + DPRINTF(sc->sc_dev, "clknode_div_register failed %x\n", err); + return (ENXIO); + } + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err); + return (ENXIO); + } + + return (0); +} + +static int +ti_divider_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI Divider Clock"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ti_divider_attach(device_t dev) +{ + struct ti_divider_softc *sc; + phandle_t node; + int err; + cell_t value; + uint32_t ti_max_div; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + node = ofw_bus_get_node(dev); + + /* Grab the content of reg properties */ + OF_getencprop(node, "reg", &value, sizeof(value)); + sc->div_def.offset = value; + + if (OF_hasprop(node, "ti,bit-shift")) { + OF_getencprop(node, "ti,bit-shift", &value, sizeof(value)); + sc->div_def.i_shift = value; + } + + if (OF_hasprop(node, "ti,index-starts-at-one")) { + sc->div_def.div_flags = CLK_DIV_ZERO_BASED; + } + + if (OF_hasprop(node, "ti,index-power-of-two")) { + /* FIXME: later */ + device_printf(sc->sc_dev, "ti,index-power-of-two - Not implemented\n"); + /* remember to update i_width a few lines below */ + } + if (OF_hasprop(node, "ti,max-div")) { + OF_getencprop(node, "ti,max-div", &value, sizeof(value)); + ti_max_div = value; + } + + if (OF_hasprop(node, "clock-output-names")) + device_printf(sc->sc_dev, "clock-output-names\n"); + if (OF_hasprop(node, "ti,dividers")) + device_printf(sc->sc_dev, "ti,dividers\n"); + if (OF_hasprop(node, "ti,min-div")) + device_printf(sc->sc_dev, "ti,min-div - Not implemented\n"); + + if (OF_hasprop(node, "ti,autoidle-shift")) + device_printf(sc->sc_dev, "ti,autoidle-shift - Not implemented\n"); + if (OF_hasprop(node, "ti,set-rate-parent")) + device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n"); + if (OF_hasprop(node, "ti,latch-bit")) + device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n"); + + /* Figure out the width from ti_max_div */ + if (sc->div_def.div_flags) + sc->div_def.i_width = fls(ti_max_div-1); + else + sc->div_def.i_width = fls(ti_max_div); + + DPRINTF(sc->sc_dev, "div_def.i_width %x\n", sc->div_def.i_width); + + read_clock_cells(sc->sc_dev, &sc->clock_cell); + + create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef); + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef); + + if (err) { + /* free_clkdef will be called in ti_divider_new_pass */ + DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + err = register_clk(sc); + + if (err) { + /* free_clkdef will be called in ti_divider_new_pass */ + DPRINTF(sc->sc_dev, "register_clk failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + sc->attach_done = true; + + free_clkdef(&sc->div_def.clkdef); + + return (bus_generic_attach(sc->sc_dev)); +} + +static int +ti_divider_detach(device_t dev) +{ + return (EBUSY); +} + +static void +ti_divider_new_pass(device_t dev) +{ + struct ti_divider_softc *sc; + int err; + + sc = device_get_softc(dev); + + if (sc->attach_done) { + return; + } + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->div_def.clkdef); + if (err) { + /* free_clkdef will be called in a later call to ti_divider_new_pass */ + DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n"); + return; + } + + err = register_clk(sc); + if (err) { + /* free_clkdef will be called in a later call to ti_divider_new_pass */ + DPRINTF(sc->sc_dev, "new_pass register_clk failed\n"); + return; + } + + sc->attach_done = true; + + free_clkdef(&sc->div_def.clkdef); +} + +static device_method_t ti_divider_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_divider_probe), + DEVMETHOD(device_attach, ti_divider_attach), + DEVMETHOD(device_detach, ti_divider_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_divider_new_pass), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ti_divider, ti_divider_driver, ti_divider_methods, + sizeof(struct ti_divider_softc)); + +static devclass_t ti_divider_devclass; + +EARLY_DRIVER_MODULE(ti_divider, simplebus, ti_divider_driver, + ti_divider_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_divider, 1); Property changes on: head/sys/arm/ti/clk/ti_divider_clock.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_dpll_clock.c =================================================================== --- head/sys/arm/ti/clk/ti_dpll_clock.c (nonexistent) +++ head/sys/arm/ti/clk/ti_dpll_clock.c (revision 363700) @@ -0,0 +1,375 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include "clock_common.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* + * Devicetree description + * Documentation/devicetree/bindings/clock/ti/dpll.txt + */ + +struct ti_dpll_softc { + device_t dev; + uint8_t dpll_type; + + bool attach_done; + struct ti_clk_dpll_def dpll_def; + + struct clock_cell_info clock_cell; + struct clkdom *clkdom; +}; + +static int ti_dpll_probe(device_t dev); +static int ti_dpll_attach(device_t dev); +static int ti_dpll_detach(device_t dev); + +#define TI_OMAP3_DPLL_CLOCK 17 +#define TI_OMAP3_DPLL_CORE_CLOCK 16 +#define TI_OMAP3_DPLL_PER_CLOCK 15 +#define TI_OMAP3_DPLL_PER_J_TYPE_CLOCK 14 +#define TI_OMAP4_DPLL_CLOCK 13 +#define TI_OMAP4_DPLL_X2_CLOCK 12 +#define TI_OMAP4_DPLL_CORE_CLOCK 11 +#define TI_OMAP4_DPLL_M4XEN_CLOCK 10 +#define TI_OMAP4_DPLL_J_TYPE_CLOCK 9 +#define TI_OMAP5_MPU_DPLL_CLOCK 8 +#define TI_AM3_DPLL_NO_GATE_CLOCK 7 +#define TI_AM3_DPLL_J_TYPE_CLOCK 6 +#define TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK 5 +#define TI_AM3_DPLL_CLOCK 4 +#define TI_AM3_DPLL_CORE_CLOCK 3 +#define TI_AM3_DPLL_X2_CLOCK 2 +#define TI_OMAP2_DPLL_CORE_CLOCK 1 +#define TI_DPLL_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,omap3-dpll-clock", TI_OMAP3_DPLL_CLOCK }, + { "ti,omap3-dpll-core-clock", TI_OMAP3_DPLL_CORE_CLOCK }, + { "ti,omap3-dpll-per-clock", TI_OMAP3_DPLL_PER_CLOCK }, + { "ti,omap3-dpll-per-j-type-clock",TI_OMAP3_DPLL_PER_J_TYPE_CLOCK }, + { "ti,omap4-dpll-clock", TI_OMAP4_DPLL_CLOCK }, + { "ti,omap4-dpll-x2-clock", TI_OMAP4_DPLL_X2_CLOCK }, + { "ti,omap4-dpll-core-clock", TI_OMAP4_DPLL_CORE_CLOCK }, + { "ti,omap4-dpll-m4xen-clock", TI_OMAP4_DPLL_M4XEN_CLOCK }, + { "ti,omap4-dpll-j-type-clock", TI_OMAP4_DPLL_J_TYPE_CLOCK }, + { "ti,omap5-mpu-dpll-clock", TI_OMAP5_MPU_DPLL_CLOCK }, + { "ti,am3-dpll-no-gate-clock", TI_AM3_DPLL_NO_GATE_CLOCK }, + { "ti,am3-dpll-j-type-clock", TI_AM3_DPLL_J_TYPE_CLOCK }, + { "ti,am3-dpll-no-gate-j-type-clock",TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK }, + { "ti,am3-dpll-clock", TI_AM3_DPLL_CLOCK }, + { "ti,am3-dpll-core-clock", TI_AM3_DPLL_CORE_CLOCK }, + { "ti,am3-dpll-x2-clock", TI_AM3_DPLL_X2_CLOCK }, + { "ti,omap2-dpll-core-clock", TI_OMAP2_DPLL_CORE_CLOCK }, + { NULL, TI_DPLL_END } +}; + +static int +register_clk(struct ti_dpll_softc *sc) { + int err; + + sc->clkdom = clkdom_create(sc->dev); + if (sc->clkdom == NULL) { + DPRINTF(sc->dev, "Failed to create clkdom\n"); + return (ENXIO); + } + + err = ti_clknode_dpll_register(sc->clkdom, &sc->dpll_def); + if (err) { + DPRINTF(sc->dev, + "ti_clknode_dpll_register failed %x\n", err); + return (ENXIO); + } + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err); + return (ENXIO); + } + + return (0); +} + +static int +ti_dpll_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI DPLL Clock"); + + return (BUS_PROBE_DEFAULT); +} + +static int +parse_dpll_reg(struct ti_dpll_softc *sc) { + ssize_t numbytes_regs; + uint32_t num_regs; + phandle_t node; + cell_t reg_cells[4]; + + if (sc->dpll_type == TI_AM3_DPLL_X2_CLOCK || + sc->dpll_type == TI_OMAP4_DPLL_X2_CLOCK) { + sc->dpll_def.ti_clksel_mult.value = 2; + sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_FIXED; + + sc->dpll_def.ti_clksel_div.value = 1; + sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_FIXED; + return (0); + } + + node = ofw_bus_get_node(sc->dev); + + numbytes_regs = OF_getproplen(node, "reg"); + num_regs = numbytes_regs / sizeof(cell_t); + + /* Sanity check */ + if (num_regs > 4) + return (ENXIO); + + OF_getencprop(node, "reg", reg_cells, numbytes_regs); + + switch (sc->dpll_type) { + case TI_AM3_DPLL_NO_GATE_CLOCK: + case TI_AM3_DPLL_J_TYPE_CLOCK: + case TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK: + case TI_AM3_DPLL_CLOCK: + case TI_AM3_DPLL_CORE_CLOCK: + case TI_AM3_DPLL_X2_CLOCK: + if (num_regs != 3) + return (ENXIO); + sc->dpll_def.ti_clkmode_offset = reg_cells[0]; + sc->dpll_def.ti_idlest_offset = reg_cells[1]; + sc->dpll_def.ti_clksel_offset = reg_cells[2]; + break; + + case TI_OMAP2_DPLL_CORE_CLOCK: + if (num_regs != 2) + return (ENXIO); + sc->dpll_def.ti_clkmode_offset = reg_cells[0]; + sc->dpll_def.ti_clksel_offset = reg_cells[1]; + break; + + default: + sc->dpll_def.ti_clkmode_offset = reg_cells[0]; + sc->dpll_def.ti_idlest_offset = reg_cells[1]; + sc->dpll_def.ti_clksel_offset = reg_cells[2]; + sc->dpll_def.ti_autoidle_offset = reg_cells[3]; + break; + } + + /* AM335x */ + if (sc->dpll_def.ti_clksel_offset == CM_CLKSEL_DPLL_PERIPH) { + sc->dpll_def.ti_clksel_mult.shift = 8; + sc->dpll_def.ti_clksel_mult.mask = 0x000FFF00; + sc->dpll_def.ti_clksel_mult.width = 12; + sc->dpll_def.ti_clksel_mult.value = 0; + sc->dpll_def.ti_clksel_mult.min_value = 2; + sc->dpll_def.ti_clksel_mult.max_value = 4095; + sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED | + TI_CLK_FACTOR_MIN_VALUE | + TI_CLK_FACTOR_MAX_VALUE; + + sc->dpll_def.ti_clksel_div.shift = 0; + sc->dpll_def.ti_clksel_div.mask = 0x000000FF; + sc->dpll_def.ti_clksel_div.width = 8; + sc->dpll_def.ti_clksel_div.value = 0; + sc->dpll_def.ti_clksel_div.min_value = 0; + sc->dpll_def.ti_clksel_div.max_value = 255; + sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE | + TI_CLK_FACTOR_MAX_VALUE; + } else { + sc->dpll_def.ti_clksel_mult.shift = 8; + sc->dpll_def.ti_clksel_mult.mask = 0x0007FF00; + sc->dpll_def.ti_clksel_mult.width = 11; + sc->dpll_def.ti_clksel_mult.value = 0; + sc->dpll_def.ti_clksel_mult.min_value = 2; + sc->dpll_def.ti_clksel_mult.max_value = 2047; + sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED | + TI_CLK_FACTOR_MIN_VALUE | + TI_CLK_FACTOR_MAX_VALUE; + + sc->dpll_def.ti_clksel_div.shift = 0; + sc->dpll_def.ti_clksel_div.mask = 0x0000007F; + sc->dpll_def.ti_clksel_div.width = 7; + sc->dpll_def.ti_clksel_div.value = 0; + sc->dpll_def.ti_clksel_div.min_value = 0; + sc->dpll_def.ti_clksel_div.max_value = 127; + sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE | + TI_CLK_FACTOR_MAX_VALUE; + } + DPRINTF(sc->dev, "clkmode %x idlest %x clksel %x autoidle %x\n", + sc->dpll_def.ti_clkmode_offset, sc->dpll_def.ti_idlest_offset, + sc->dpll_def.ti_clksel_offset, + sc->dpll_def.ti_autoidle_offset); + + return (0); +} +static int +ti_dpll_attach(device_t dev) +{ + struct ti_dpll_softc *sc; + phandle_t node; + int err; + + sc = device_get_softc(dev); + sc->dev = dev; + + sc->dpll_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + node = ofw_bus_get_node(dev); + + /* Grab the content of reg properties */ + parse_dpll_reg(sc); + + /* default flags (OMAP4&AM335x) not present in the dts at moment */ + sc->dpll_def.ti_clkmode_flags = MN_BYPASS_MODE_FLAG | LOCK_MODE_FLAG; + + if (OF_hasprop(node, "ti,low-power-stop")) { + sc->dpll_def.ti_clkmode_flags |= LOW_POWER_STOP_MODE_FLAG; + } + if (OF_hasprop(node, "ti,low-power-bypass")) { + sc->dpll_def.ti_clkmode_flags |= IDLE_BYPASS_LOW_POWER_MODE_FLAG; + } + if (OF_hasprop(node, "ti,lock")) { + sc->dpll_def.ti_clkmode_flags |= LOCK_MODE_FLAG; + } + + read_clock_cells(sc->dev, &sc->clock_cell); + + create_clkdef(sc->dev, &sc->clock_cell, &sc->dpll_def.clkdef); + + err = find_parent_clock_names(sc->dev, &sc->clock_cell, + &sc->dpll_def.clkdef); + + if (err) { + /* free_clkdef will be called in ti_dpll_new_pass */ + DPRINTF(sc->dev, "find_parent_clock_names failed\n"); + return (bus_generic_attach(sc->dev)); + } + + err = register_clk(sc); + + if (err) { + /* free_clkdef will be called in ti_dpll_new_pass */ + DPRINTF(sc->dev, "register_clk failed\n"); + return (bus_generic_attach(sc->dev)); + } + + sc->attach_done = true; + + free_clkdef(&sc->dpll_def.clkdef); + + return (bus_generic_attach(sc->dev)); +} + +static int +ti_dpll_detach(device_t dev) +{ + return (EBUSY); +} + +static void +ti_dpll_new_pass(device_t dev) +{ + struct ti_dpll_softc *sc; + int err; + + sc = device_get_softc(dev); + + if (sc->attach_done) { + return; + } + + err = find_parent_clock_names(sc->dev, &sc->clock_cell, + &sc->dpll_def.clkdef); + if (err) { + /* free_clkdef will be called in a later call to ti_dpll_new_pass */ + DPRINTF(sc->dev, + "new_pass find_parent_clock_names failed\n"); + return; + } + + err = register_clk(sc); + if (err) { + /* free_clkdef will be called in a later call to ti_dpll_new_pass */ + DPRINTF(sc->dev, "new_pass register_clk failed\n"); + return; + } + + sc->attach_done = true; + free_clkdef(&sc->dpll_def.clkdef); +} + +static device_method_t ti_dpll_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_dpll_probe), + DEVMETHOD(device_attach, ti_dpll_attach), + DEVMETHOD(device_detach, ti_dpll_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_dpll_new_pass), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods, + sizeof(struct ti_dpll_softc)); + +static devclass_t ti_dpll_devclass; + +EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver, + ti_dpll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_dpll, 1); Property changes on: head/sys/arm/ti/clk/ti_dpll_clock.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_gate_clock.c =================================================================== --- head/sys/arm/ti/clk/ti_gate_clock.c (nonexistent) +++ head/sys/arm/ti/clk/ti_gate_clock.c (revision 363700) @@ -0,0 +1,266 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clock_common.h" + +#define DEBUG_GATE 0 + +#if DEBUG_GATE +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* + * Devicetree description + * Documentation/devicetree/bindings/clock/ti/gate.txt + */ + +struct ti_gate_softc { + device_t sc_dev; + bool attach_done; + uint8_t sc_type; + + struct clk_gate_def gate_def; + struct clock_cell_info clock_cell; + struct clkdom *clkdom; +}; + +static int ti_gate_probe(device_t dev); +static int ti_gate_attach(device_t dev); +static int ti_gate_detach(device_t dev); + +#define TI_GATE_CLOCK 7 +#define TI_WAIT_GATE_CLOCK 6 +#define TI_DSS_GATE_CLOCK 5 +#define TI_AM35XX_GATE_CLOCK 4 +#define TI_CLKDM_GATE_CLOCK 3 +#define TI_HSDIV_GATE_CLOCK 2 +#define TI_COMPOSITE_NO_WAIT_GATE_CLOCK 1 +#define TI_GATE_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,gate-clock", TI_GATE_CLOCK }, + { "ti,wait-gate-clock", TI_WAIT_GATE_CLOCK }, + { "ti,dss-gate-clock", TI_DSS_GATE_CLOCK }, + { "ti,am35xx-gate-clock", TI_AM35XX_GATE_CLOCK }, + { "ti,clkdm-gate-clock", TI_CLKDM_GATE_CLOCK }, + { "ti,hsdiv-gate-cloc", TI_HSDIV_GATE_CLOCK }, + { "ti,composite-no-wait-gate-clock", TI_COMPOSITE_NO_WAIT_GATE_CLOCK }, + { NULL, TI_GATE_END } +}; + +static int +ti_gate_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI Gate Clock"); + + return (BUS_PROBE_DEFAULT); +} + +static int +register_clk(struct ti_gate_softc *sc) { + int err; + sc->clkdom = clkdom_create(sc->sc_dev); + if (sc->clkdom == NULL) { + DPRINTF(sc->sc_dev, "Failed to create clkdom\n"); + return ENXIO; + } + + err = clknode_gate_register(sc->clkdom, &sc->gate_def); + if (err) { + DPRINTF(sc->sc_dev, "clknode_gate_register failed %x\n", err); + return ENXIO; + } + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err); + return ENXIO; + } + + return (0); +} + +static int +ti_gate_attach(device_t dev) +{ + struct ti_gate_softc *sc; + phandle_t node; + int err; + cell_t value; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + node = ofw_bus_get_node(dev); + + /* Get the compatible type */ + sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + /* Get the content of reg properties */ + if (sc->sc_type != TI_CLKDM_GATE_CLOCK) { + OF_getencprop(node, "reg", &value, sizeof(value)); + sc->gate_def.offset = value; + } +#if DEBUG_GATE + else { + DPRINTF(sc->sc_dev, "no reg (TI_CLKDM_GATE_CLOCK)\n"); + } +#endif + + if (OF_hasprop(node, "ti,bit-shift")) { + OF_getencprop(node, "ti,bit-shift", &value, sizeof(value)); + sc->gate_def.shift = value; + DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->gate_def.shift); + } + if (OF_hasprop(node, "ti,set-bit-to-disable")) { + sc->gate_def.on_value = 0; + sc->gate_def.off_value = 1; + DPRINTF(sc->sc_dev, + "on_value = 0, off_value = 1 (ti,set-bit-to-disable)\n"); + } else { + sc->gate_def.on_value = 1; + sc->gate_def.off_value = 0; + DPRINTF(sc->sc_dev, "on_value = 1, off_value = 0\n"); + } + + sc->gate_def.gate_flags = 0x0; + + read_clock_cells(sc->sc_dev, &sc->clock_cell); + + create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef); + + /* Calculate mask */ + sc->gate_def.mask = (1 << fls(sc->clock_cell.num_real_clocks)) - 1; + DPRINTF(sc->sc_dev, "num_real_clocks %x gate_def.mask %x\n", + sc->clock_cell.num_real_clocks, sc->gate_def.mask); + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef); + + if (err) { + /* free_clkdef will be called in ti_gate_new_pass */ + DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + err = register_clk(sc); + + if (err) { + /* free_clkdef will be called in ti_gate_new_pass */ + DPRINTF(sc->sc_dev, "register_clk failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + sc->attach_done = true; + + free_clkdef(&sc->gate_def.clkdef); + + return (bus_generic_attach(sc->sc_dev)); +} + +static int +ti_gate_detach(device_t dev) +{ + return (EBUSY); +} + +static void +ti_gate_new_pass(device_t dev) { + struct ti_gate_softc *sc; + int err; + + sc = device_get_softc(dev); + + if (sc->attach_done) { + return; + } + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef); + if (err) { + /* free_clkdef will be called in later call to ti_gate_new_pass */ + DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n"); + return; + } + + err = register_clk(sc); + if (err) { + /* free_clkdef will be called in later call to ti_gate_new_pass */ + DPRINTF(sc->sc_dev, "new_pass register_clk failed\n"); + return; + } + + sc->attach_done = true; + + free_clkdef(&sc->gate_def.clkdef); +} + +static device_method_t ti_gate_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_gate_probe), + DEVMETHOD(device_attach, ti_gate_attach), + DEVMETHOD(device_detach, ti_gate_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_gate_new_pass), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ti_gate, ti_gate_driver, ti_gate_methods, + sizeof(struct ti_gate_softc)); + +static devclass_t ti_gate_devclass; + +EARLY_DRIVER_MODULE(ti_gate, simplebus, ti_gate_driver, + ti_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_gate, 1); Property changes on: head/sys/arm/ti/clk/ti_gate_clock.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/clk/ti_mux_clock.c =================================================================== --- head/sys/arm/ti/clk/ti_mux_clock.c (nonexistent) +++ head/sys/arm/ti/clk/ti_mux_clock.c (revision 363700) @@ -0,0 +1,249 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clock_common.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* + * Devicetree description + * Documentation/devicetree/bindings/clock/ti/mux.txt + */ + +struct ti_mux_softc { + device_t sc_dev; + bool attach_done; + + struct clk_mux_def mux_def; + struct clock_cell_info clock_cell; + struct clkdom *clkdom; +}; + +static int ti_mux_probe(device_t dev); +static int ti_mux_attach(device_t dev); +static int ti_mux_detach(device_t dev); + +#define TI_MUX_CLOCK 2 +#define TI_COMPOSITE_MUX_CLOCK 1 +#define TI_MUX_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,mux-clock", TI_MUX_CLOCK }, + { "ti,composite-mux-clock", TI_COMPOSITE_MUX_CLOCK }, + { NULL, TI_MUX_END } +}; + +static int +ti_mux_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI Mux Clock"); + + return (BUS_PROBE_DEFAULT); +} + +static int +register_clk(struct ti_mux_softc *sc) { + int err; + + sc->clkdom = clkdom_create(sc->sc_dev); + if (sc->clkdom == NULL) { + DPRINTF(sc->sc_dev, "Failed to create clkdom\n"); + return ENXIO; + } + + err = clknode_mux_register(sc->clkdom, &sc->mux_def); + if (err) { + DPRINTF(sc->sc_dev, "clknode_mux_register failed %x\n", err); + return ENXIO; + } + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err); + return ENXIO; + } + + return 0; +} + +static int +ti_mux_attach(device_t dev) +{ + struct ti_mux_softc *sc; + phandle_t node; + int err; + cell_t value; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + node = ofw_bus_get_node(dev); + + /* Grab the content of reg properties */ + OF_getencprop(node, "reg", &value, sizeof(value)); + sc->mux_def.offset = value; + + if (OF_hasprop(node, "ti,bit-shift")) { + OF_getencprop(node, "ti,bit-shift", &value, sizeof(value)); + sc->mux_def.shift = value; + DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->mux_def.shift); + } + if (OF_hasprop(node, "ti,index-starts-at-one")) { + /* FIXME: Add support in dev/extres/clk */ + /*sc->mux_def.mux_flags = ... */ + device_printf(sc->sc_dev, "ti,index-starts-at-one - Not implemented\n"); + } + + if (OF_hasprop(node, "ti,set-rate-parent")) + device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n"); + if (OF_hasprop(node, "ti,latch-bit")) + device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n"); + + read_clock_cells(sc->sc_dev, &sc->clock_cell); + + create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); + + /* Figure out the width from ti_max_div */ + if (sc->mux_def.mux_flags) + sc->mux_def.width = fls(sc->clock_cell.num_real_clocks-1); + else + sc->mux_def.width = fls(sc->clock_cell.num_real_clocks); + + DPRINTF(sc->sc_dev, "sc->clock_cell.num_real_clocks %x def.width %x\n", + sc->clock_cell.num_real_clocks, sc->mux_def.width); + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); + + if (err) { + /* free_clkdef will be called in ti_mux_new_pass */ + DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + err = register_clk(sc); + + if (err) { + /* free_clkdef will be called in ti_mux_new_pass */ + DPRINTF(sc->sc_dev, "register_clk failed\n"); + return (bus_generic_attach(sc->sc_dev)); + } + + sc->attach_done = true; + + free_clkdef(&sc->mux_def.clkdef); + + return (bus_generic_attach(sc->sc_dev)); +} + +static void +ti_mux_new_pass(device_t dev) +{ + struct ti_mux_softc *sc; + int err; + + sc = device_get_softc(dev); + + if (sc->attach_done) { + return; + } + + err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); + if (err) { + /* free_clkdef will be called in later call to ti_mux_new_pass */ + DPRINTF(sc->sc_dev, "ti_mux_new_pass find_parent_clock_names failed\n"); + return; + } + + err = register_clk(sc); + if (err) { + /* free_clkdef will be called in later call to ti_mux_new_pass */ + DPRINTF(sc->sc_dev, "ti_mux_new_pass register_clk failed\n"); + return; + } + + sc->attach_done = true; + + free_clkdef(&sc->mux_def.clkdef); +} + +static int +ti_mux_detach(device_t dev) +{ + return (EBUSY); +} + +static device_method_t ti_mux_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_mux_probe), + DEVMETHOD(device_attach, ti_mux_attach), + DEVMETHOD(device_detach, ti_mux_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_mux_new_pass), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ti_mux, ti_mux_driver, ti_mux_methods, + sizeof(struct ti_mux_softc)); + +static devclass_t ti_mux_devclass; + +EARLY_DRIVER_MODULE(ti_mux, simplebus, ti_mux_driver, + ti_mux_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_mux, 1); Property changes on: head/sys/arm/ti/clk/ti_mux_clock.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/cpsw/if_cpsw.c =================================================================== --- head/sys/arm/ti/cpsw/if_cpsw.c (revision 363699) +++ head/sys/arm/ti/cpsw/if_cpsw.c (revision 363700) @@ -1,3006 +1,3028 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Damjan Marion * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /* * TI Common Platform Ethernet Switch (CPSW) Driver * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs. * * This controller is documented in the AM335x Technical Reference * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM. * * It is basically a single Ethernet port (port 0) wired internally to * a 3-port store-and-forward switch connected to two independent * "sliver" controllers (port 1 and port 2). You can operate the * controller in a variety of different ways by suitably configuring * the slivers and the Address Lookup Engine (ALE) that routes packets * between the ports. * * This code was developed and tested on a BeagleBone with * an AM335x SoC. */ #include __FBSDID("$FreeBSD$"); #include "opt_cpsw.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include +#include "syscon_if.h" #include #include #include #include #include #include #ifdef CPSW_ETHERSWITCH #include #include "etherswitch_if.h" #endif #include "if_cpswreg.h" #include "if_cpswvar.h" #include "miibus_if.h" /* Device probe/attach/detach. */ static int cpsw_probe(device_t); static int cpsw_attach(device_t); static int cpsw_detach(device_t); static int cpswp_probe(device_t); static int cpswp_attach(device_t); static int cpswp_detach(device_t); static phandle_t cpsw_get_node(device_t, device_t); /* Device Init/shutdown. */ static int cpsw_shutdown(device_t); static void cpswp_init(void *); static void cpswp_init_locked(void *); static void cpswp_stop_locked(struct cpswp_softc *); /* Device Suspend/Resume. */ static int cpsw_suspend(device_t); static int cpsw_resume(device_t); /* Ioctl. */ static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data); static int cpswp_miibus_readreg(device_t, int phy, int reg); static int cpswp_miibus_writereg(device_t, int phy, int reg, int value); static void cpswp_miibus_statchg(device_t); /* Send/Receive packets. */ static void cpsw_intr_rx(void *arg); static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *); static void cpsw_rx_enqueue(struct cpsw_softc *); static void cpswp_start(struct ifnet *); static void cpsw_intr_tx(void *); static void cpswp_tx_enqueue(struct cpswp_softc *); static int cpsw_tx_dequeue(struct cpsw_softc *); /* Misc interrupts and watchdog. */ static void cpsw_intr_rx_thresh(void *); static void cpsw_intr_misc(void *); static void cpswp_tick(void *); static void cpswp_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int cpswp_ifmedia_upd(struct ifnet *); static void cpsw_tx_watchdog(void *); /* ALE support */ static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *); static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *); static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *); static void cpsw_ale_dump_table(struct cpsw_softc *); static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int, int, int); static int cpswp_ale_update_addresses(struct cpswp_softc *, int); /* Statistics and sysctls. */ static void cpsw_add_sysctls(struct cpsw_softc *); static void cpsw_stats_collect(struct cpsw_softc *); static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS); #ifdef CPSW_ETHERSWITCH static etherswitch_info_t *cpsw_getinfo(device_t); static int cpsw_getport(device_t, etherswitch_port_t *); static int cpsw_setport(device_t, etherswitch_port_t *); static int cpsw_getconf(device_t, etherswitch_conf_t *); static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *); static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *); static int cpsw_readreg(device_t, int); static int cpsw_writereg(device_t, int, int); static int cpsw_readphy(device_t, int, int); static int cpsw_writephy(device_t, int, int, int); #endif /* * Arbitrary limit on number of segments in an mbuf to be transmitted. * Packets with more segments than this will be defragmented before * they are queued. */ #define CPSW_TXFRAGS 16 /* Shared resources. */ static device_method_t cpsw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpsw_probe), DEVMETHOD(device_attach, cpsw_attach), DEVMETHOD(device_detach, cpsw_detach), DEVMETHOD(device_shutdown, cpsw_shutdown), DEVMETHOD(device_suspend, cpsw_suspend), DEVMETHOD(device_resume, cpsw_resume), /* Bus interface */ DEVMETHOD(bus_add_child, device_add_child_ordered), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, cpsw_get_node), #ifdef CPSW_ETHERSWITCH /* etherswitch interface */ DEVMETHOD(etherswitch_getinfo, cpsw_getinfo), DEVMETHOD(etherswitch_readreg, cpsw_readreg), DEVMETHOD(etherswitch_writereg, cpsw_writereg), DEVMETHOD(etherswitch_readphyreg, cpsw_readphy), DEVMETHOD(etherswitch_writephyreg, cpsw_writephy), DEVMETHOD(etherswitch_getport, cpsw_getport), DEVMETHOD(etherswitch_setport, cpsw_setport), DEVMETHOD(etherswitch_getvgroup, cpsw_getvgroup), DEVMETHOD(etherswitch_setvgroup, cpsw_setvgroup), DEVMETHOD(etherswitch_getconf, cpsw_getconf), #endif DEVMETHOD_END }; static driver_t cpsw_driver = { "cpswss", cpsw_methods, sizeof(struct cpsw_softc), }; static devclass_t cpsw_devclass; DRIVER_MODULE(cpswss, simplebus, cpsw_driver, cpsw_devclass, 0, 0); /* Port/Slave resources. */ static device_method_t cpswp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpswp_probe), DEVMETHOD(device_attach, cpswp_attach), DEVMETHOD(device_detach, cpswp_detach), /* MII interface */ DEVMETHOD(miibus_readreg, cpswp_miibus_readreg), DEVMETHOD(miibus_writereg, cpswp_miibus_writereg), DEVMETHOD(miibus_statchg, cpswp_miibus_statchg), DEVMETHOD_END }; static driver_t cpswp_driver = { "cpsw", cpswp_methods, sizeof(struct cpswp_softc), }; static devclass_t cpswp_devclass; #ifdef CPSW_ETHERSWITCH DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, etherswitch_devclass, 0, 0); MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1); #endif DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0); DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(cpsw, ether, 1, 1, 1); MODULE_DEPEND(cpsw, miibus, 1, 1, 1); #ifdef CPSW_ETHERSWITCH static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS]; #endif static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 }; static struct resource_spec irq_res_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static struct { void (*cb)(void *); } cpsw_intr_cb[] = { { cpsw_intr_rx_thresh }, { cpsw_intr_rx }, { cpsw_intr_tx }, { cpsw_intr_misc }, }; /* Number of entries here must match size of stats * array in struct cpswp_softc. */ static struct cpsw_stat { int reg; char *oid; } cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = { {0x00, "GoodRxFrames"}, {0x04, "BroadcastRxFrames"}, {0x08, "MulticastRxFrames"}, {0x0C, "PauseRxFrames"}, {0x10, "RxCrcErrors"}, {0x14, "RxAlignErrors"}, {0x18, "OversizeRxFrames"}, {0x1c, "RxJabbers"}, {0x20, "ShortRxFrames"}, {0x24, "RxFragments"}, {0x30, "RxOctets"}, {0x34, "GoodTxFrames"}, {0x38, "BroadcastTxFrames"}, {0x3c, "MulticastTxFrames"}, {0x40, "PauseTxFrames"}, {0x44, "DeferredTxFrames"}, {0x48, "CollisionsTxFrames"}, {0x4c, "SingleCollisionTxFrames"}, {0x50, "MultipleCollisionTxFrames"}, {0x54, "ExcessiveCollisions"}, {0x58, "LateCollisions"}, {0x5c, "TxUnderrun"}, {0x60, "CarrierSenseErrors"}, {0x64, "TxOctets"}, {0x68, "RxTx64OctetFrames"}, {0x6c, "RxTx65to127OctetFrames"}, {0x70, "RxTx128to255OctetFrames"}, {0x74, "RxTx256to511OctetFrames"}, {0x78, "RxTx512to1024OctetFrames"}, {0x7c, "RxTx1024upOctetFrames"}, {0x80, "NetOctets"}, {0x84, "RxStartOfFrameOverruns"}, {0x88, "RxMiddleOfFrameOverruns"}, {0x8c, "RxDmaOverruns"} }; /* * Basic debug support. */ static void cpsw_debugf_head(const char *funcname) { int t = (int)(time_second % (24 * 60 * 60)); printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname); } static void cpsw_debugf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } #define CPSW_DEBUGF(_sc, a) do { \ if ((_sc)->debug) { \ cpsw_debugf_head(__func__); \ cpsw_debugf a; \ } \ } while (0) /* * Locking macros */ #define CPSW_TX_LOCK(sc) do { \ mtx_assert(&(sc)->rx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->tx.lock); \ } while (0) #define CPSW_TX_UNLOCK(sc) mtx_unlock(&(sc)->tx.lock) #define CPSW_TX_LOCK_ASSERT(sc) mtx_assert(&(sc)->tx.lock, MA_OWNED) #define CPSW_RX_LOCK(sc) do { \ mtx_assert(&(sc)->tx.lock, MA_NOTOWNED); \ mtx_lock(&(sc)->rx.lock); \ } while (0) #define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock) #define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED) #define CPSW_PORT_LOCK(_sc) do { \ mtx_assert(&(_sc)->lock, MA_NOTOWNED); \ mtx_lock(&(_sc)->lock); \ } while (0) #define CPSW_PORT_UNLOCK(_sc) mtx_unlock(&(_sc)->lock) #define CPSW_PORT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->lock, MA_OWNED) /* * Read/Write macros */ #define cpsw_read_4(_sc, _reg) bus_read_4((_sc)->mem_res, (_reg)) #define cpsw_write_4(_sc, _reg, _val) \ bus_write_4((_sc)->mem_res, (_reg), (_val)) #define cpsw_cpdma_bd_offset(i) (CPSW_CPPI_RAM_OFFSET + ((i)*16)) #define cpsw_cpdma_bd_paddr(sc, slot) \ BUS_SPACE_PHYSADDR(sc->mem_res, slot->bd_offset) #define cpsw_cpdma_read_bd(sc, slot, val) \ bus_read_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd(sc, slot, val) \ bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4) #define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \ cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot)) #define cpsw_cpdma_write_bd_flags(sc, slot, val) \ bus_write_2(sc->mem_res, slot->bd_offset + 14, val) #define cpsw_cpdma_read_bd_flags(sc, slot) \ bus_read_2(sc->mem_res, slot->bd_offset + 14) #define cpsw_write_hdp_slot(sc, queue, slot) \ cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot)) #define CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0)) #define cpsw_read_cp(sc, queue) \ cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET) #define cpsw_write_cp(sc, queue, val) \ cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val)) #define cpsw_write_cp_slot(sc, queue, slot) \ cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot)) #if 0 /* XXX temporary function versions for debugging. */ static void cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t reg = queue->hdp_offset; uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg))); cpsw_write_4(sc, reg, v); } static void cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot) { uint32_t v = cpsw_cpdma_bd_paddr(sc, slot); CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue))); cpsw_write_cp(sc, queue, v); } #endif /* * Expanded dump routines for verbose debugging. */ static void cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ", "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun", "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1", "Port0"}; struct cpsw_cpdma_bd bd; const char *sep; int i; cpsw_cpdma_read_bd(sc, slot, &bd); printf("BD Addr : 0x%08x Next : 0x%08x\n", cpsw_cpdma_bd_paddr(sc, slot), bd.next); printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen); printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen); printf(" Flags: "); sep = ""; for (i = 0; i < 16; ++i) { if (bd.flags & (1 << (15 - i))) { printf("%s%s", sep, flags[i]); sep = ","; } } printf("\n"); if (slot->mbuf) { printf(" Ether: %14D\n", (char *)(slot->mbuf->m_data), " "); printf(" Packet: %16D\n", (char *)(slot->mbuf->m_data) + 14, " "); } } #define CPSW_DUMP_SLOT(cs, slot) do { \ IF_DEBUG(sc) { \ cpsw_dump_slot(sc, slot); \ } \ } while (0) static void cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q) { struct cpsw_slot *slot; int i = 0; int others = 0; STAILQ_FOREACH(slot, q, next) { if (i > CPSW_TXFRAGS) ++others; else cpsw_dump_slot(sc, slot); ++i; } if (others) printf(" ... and %d more.\n", others); printf("\n"); } #define CPSW_DUMP_QUEUE(sc, q) do { \ IF_DEBUG(sc) { \ cpsw_dump_queue(sc, q); \ } \ } while (0) static void cpsw_init_slots(struct cpsw_softc *sc) { struct cpsw_slot *slot; int i; STAILQ_INIT(&sc->avail); /* Put the slot descriptors onto the global avail list. */ for (i = 0; i < nitems(sc->_slots); i++) { slot = &sc->_slots[i]; slot->bd_offset = cpsw_cpdma_bd_offset(i); STAILQ_INSERT_TAIL(&sc->avail, slot, next); } } static int cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested) { const int max_slots = nitems(sc->_slots); struct cpsw_slot *slot; int i; if (requested < 0) requested = max_slots; for (i = 0; i < requested; ++i) { slot = STAILQ_FIRST(&sc->avail); if (slot == NULL) return (0); if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) { device_printf(sc->dev, "failed to create dmamap\n"); return (ENOMEM); } STAILQ_REMOVE_HEAD(&sc->avail, next); STAILQ_INSERT_TAIL(&queue->avail, slot, next); ++queue->avail_queue_len; ++queue->queue_slots; } return (0); } static void cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot) { int error; if (slot->dmamap) { if (slot->mbuf) bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap); KASSERT(error == 0, ("Mapping still active")); slot->dmamap = NULL; } if (slot->mbuf) { m_freem(slot->mbuf); slot->mbuf = NULL; } } static void cpsw_reset(struct cpsw_softc *sc) { int i; callout_stop(&sc->watchdog.callout); /* Reset RMII/RGMII wrapper. */ cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1) ; /* Disable TX and RX interrupts for all cores. */ for (i = 0; i < 3; ++i) { cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00); } /* Reset CPSW subsystem. */ cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1) ; /* Reset Sliver port 1 and 2 */ for (i = 0; i < 2; i++) { /* Reset */ cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1); while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1) ; } /* Reset DMA controller. */ cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1); while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1) ; /* Disable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0); /* Clear all queues. */ for (i = 0; i < 8; i++) { cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0); } /* Clear all interrupt Masks */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); } static void cpsw_init(struct cpsw_softc *sc) { struct cpsw_slot *slot; uint32_t reg; /* Disable the interrupt pacing. */ reg = cpsw_read_4(sc, CPSW_WR_INT_CONTROL); reg &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK); cpsw_write_4(sc, CPSW_WR_INT_CONTROL, reg); /* Clear ALE */ cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL); /* Enable ALE */ reg = CPSW_ALE_CTL_ENABLE; if (sc->dualemac) reg |= CPSW_ALE_CTL_VLAN_AWARE; cpsw_write_4(sc, CPSW_ALE_CONTROL, reg); /* Set Host Port Mapping. */ cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); /* Initialize ALE: set host port to forwarding(3). */ cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD); cpsw_write_4(sc, CPSW_SS_PTYPE, 0); /* Enable statistics for ports 0, 1 and 2 */ cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7); /* Turn off flow control. */ cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0); /* Make IP hdr aligned with 4 */ cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2); /* Initialize RX Buffer Descriptors */ cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), 0); cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0); /* Enable TX & RX DMA */ cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1); cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1); /* Enable Interrupts for core 0 */ cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_TX_EN(0), 0xFF); cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F); /* Enable host Error Interrupt */ cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3); /* Enable interrupts for RX and TX on Channel 0 */ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, CPSW_CPDMA_RX_INT(0) | CPSW_CPDMA_RX_INT_THRESH(0)); cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_SET, 1); /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff); /* Select MII in GMII_SEL, Internal Delay mode */ //ti_scm_reg_write_4(0x650, 0); /* Initialize active queues. */ slot = STAILQ_FIRST(&sc->tx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->tx, slot); slot = STAILQ_FIRST(&sc->rx.active); if (slot != NULL) cpsw_write_hdp_slot(sc, &sc->rx, slot); cpsw_rx_enqueue(sc); cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), sc->rx.active_queue_len); cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), CPSW_TXFRAGS); /* Activate network interface. */ sc->rx.running = 1; sc->tx.running = 1; sc->watchdog.timer = 0; callout_init(&sc->watchdog.callout, 0); callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); } /* * * Device Probe, Attach, Detach. * */ static int cpsw_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,cpsw")) return (ENXIO); device_set_desc(dev, "3-port Switch Ethernet Subsystem"); return (BUS_PROBE_DEFAULT); } static int cpsw_intr_attach(struct cpsw_softc *sc) { int i; for (i = 0; i < CPSW_INTR_COUNT; i++) { if (bus_setup_intr(sc->dev, sc->irq_res[i], INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_cb[i].cb, sc, &sc->ih_cookie[i]) != 0) { return (-1); } } return (0); } static void cpsw_intr_detach(struct cpsw_softc *sc) { int i; for (i = 0; i < CPSW_INTR_COUNT; i++) { if (sc->ih_cookie[i]) { bus_teardown_intr(sc->dev, sc->irq_res[i], sc->ih_cookie[i]); } } } static int cpsw_get_fdt_data(struct cpsw_softc *sc, int port) { char *name; int len, phy, vlan; pcell_t phy_id[3], vlan_id; phandle_t child; unsigned long mdio_child_addr; /* Find any slave with phy-handle/phy_id */ phy = -1; vlan = -1; for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) { if (OF_getprop_alloc(child, "name", (void **)&name) < 0) continue; if (sscanf(name, "slave@%lx", &mdio_child_addr) != 1) { OF_prop_free(name); continue; } OF_prop_free(name); if (mdio_child_addr != slave_mdio_addr[port] && mdio_child_addr != (slave_mdio_addr[port] & 0xFFF)) continue; if (fdt_get_phyaddr(child, NULL, &phy, NULL) != 0){ /* Users with old DTB will have phy_id instead */ phy = -1; len = OF_getproplen(child, "phy_id"); if (len / sizeof(pcell_t) == 2) { /* Get phy address from fdt */ if (OF_getencprop(child, "phy_id", phy_id, len) > 0) phy = phy_id[1]; } } len = OF_getproplen(child, "dual_emac_res_vlan"); if (len / sizeof(pcell_t) == 1) { /* Get phy address from fdt */ if (OF_getencprop(child, "dual_emac_res_vlan", &vlan_id, len) > 0) { vlan = vlan_id; } } break; } if (phy == -1) return (ENXIO); sc->port[port].phy = phy; sc->port[port].vlan = vlan; return (0); } static int cpsw_attach(device_t dev) { int error, i; struct cpsw_softc *sc; uint32_t reg; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); getbinuptime(&sc->attach_uptime); if (OF_getencprop(sc->node, "active_slave", &sc->active_slave, sizeof(sc->active_slave)) <= 0) { sc->active_slave = 0; } if (sc->active_slave > 1) sc->active_slave = 1; if (OF_hasprop(sc->node, "dual_emac")) sc->dualemac = 1; for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; if (cpsw_get_fdt_data(sc, i) != 0) { device_printf(dev, "failed to get PHY address from FDT\n"); return (ENXIO); } } /* Initialize mutexes */ mtx_init(&sc->tx.lock, device_get_nameunit(dev), "cpsw TX lock", MTX_DEF); mtx_init(&sc->rx.lock, device_get_nameunit(dev), "cpsw RX lock", MTX_DEF); /* Allocate IRQ resources */ error = bus_alloc_resources(dev, irq_res_spec, sc->irq_res); if (error) { device_printf(dev, "could not allocate IRQ resources\n"); cpsw_detach(dev); return (ENXIO); } sc->mem_rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(sc->dev, "failed to allocate memory resource\n"); cpsw_detach(dev); return (ENXIO); } reg = cpsw_read_4(sc, CPSW_SS_IDVER); device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7), reg & 0xFF, (reg >> 11) & 0x1F); cpsw_add_sysctls(sc); /* Allocate a busdma tag and DMA safe memory for mbufs. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ MCLBYTES, CPSW_TXFRAGS, /* maxsize, nsegments */ MCLBYTES, 0, /* maxsegsz, flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->mbuf_dtag); /* dmatag */ if (error) { device_printf(dev, "bus_dma_tag_create failed\n"); cpsw_detach(dev); return (error); } /* Allocate a NULL buffer for padding. */ sc->nullpad = malloc(ETHER_MIN_LEN, M_DEVBUF, M_WAITOK | M_ZERO); cpsw_init_slots(sc); /* Allocate slots to TX and RX queues. */ STAILQ_INIT(&sc->rx.avail); STAILQ_INIT(&sc->rx.active); STAILQ_INIT(&sc->tx.avail); STAILQ_INIT(&sc->tx.active); // For now: 128 slots to TX, rest to RX. // XXX TODO: start with 32/64 and grow dynamically based on demand. if (cpsw_add_slots(sc, &sc->tx, 128) || cpsw_add_slots(sc, &sc->rx, -1)) { device_printf(dev, "failed to allocate dmamaps\n"); cpsw_detach(dev); return (ENOMEM); } device_printf(dev, "Initial queue size TX=%d RX=%d\n", sc->tx.queue_slots, sc->rx.queue_slots); sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0); sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0); if (cpsw_intr_attach(sc) == -1) { device_printf(dev, "failed to setup interrupts\n"); cpsw_detach(dev); return (ENXIO); } #ifdef CPSW_ETHERSWITCH for (i = 0; i < CPSW_VLANS; i++) cpsw_vgroups[i].vid = -1; #endif /* Reset the controller. */ cpsw_reset(sc); cpsw_init(sc); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; sc->port[i].dev = device_add_child(dev, "cpsw", i); if (sc->port[i].dev == NULL) { cpsw_detach(dev); return (ENXIO); } } bus_generic_probe(dev); bus_generic_attach(dev); return (0); } static int cpsw_detach(device_t dev) { struct cpsw_softc *sc; int error, i; bus_generic_detach(dev); sc = device_get_softc(dev); for (i = 0; i < CPSW_PORTS; i++) { if (sc->port[i].dev) device_delete_child(dev, sc->port[i].dev); } if (device_is_attached(dev)) { callout_stop(&sc->watchdog.callout); callout_drain(&sc->watchdog.callout); } /* Stop and release all interrupts */ cpsw_intr_detach(sc); /* Free dmamaps and mbufs */ for (i = 0; i < nitems(sc->_slots); ++i) cpsw_free_slot(sc, &sc->_slots[i]); /* Free null padding buffer. */ if (sc->nullpad) free(sc->nullpad, M_DEVBUF); /* Free DMA tag */ if (sc->mbuf_dtag) { error = bus_dma_tag_destroy(sc->mbuf_dtag); KASSERT(error == 0, ("Unable to destroy DMA tag")); } /* Free IO memory handler */ if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); bus_release_resources(dev, irq_res_spec, sc->irq_res); /* Destroy mutexes */ mtx_destroy(&sc->rx.lock); mtx_destroy(&sc->tx.lock); /* Detach the switch device, if present. */ error = bus_generic_detach(dev); if (error != 0) return (error); return (device_delete_children(dev)); } static phandle_t cpsw_get_node(device_t bus, device_t dev) { /* Share controller node with port device. */ return (ofw_bus_get_node(bus)); } static int cpswp_probe(device_t dev) { if (device_get_unit(dev) > 1) { device_printf(dev, "Only two ports are supported.\n"); return (ENXIO); } device_set_desc(dev, "Ethernet Switch Port"); return (BUS_PROBE_DEFAULT); } static int cpswp_attach(device_t dev) { int error; struct ifnet *ifp; struct cpswp_softc *sc; uint32_t reg; uint8_t mac_addr[ETHER_ADDR_LEN]; + phandle_t opp_table; + struct syscon *syscon; sc = device_get_softc(dev); sc->dev = dev; sc->pdev = device_get_parent(dev); sc->swsc = device_get_softc(sc->pdev); sc->unit = device_get_unit(dev); sc->phy = sc->swsc->port[sc->unit].phy; sc->vlan = sc->swsc->port[sc->unit].vlan; if (sc->swsc->dualemac && sc->vlan == -1) sc->vlan = sc->unit + 1; if (sc->unit == 0) { sc->physel = MDIOUSERPHYSEL0; sc->phyaccess = MDIOUSERACCESS0; } else { sc->physel = MDIOUSERPHYSEL1; sc->phyaccess = MDIOUSERACCESS1; } mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock", MTX_DEF); /* Allocate network interface */ ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { cpswp_detach(dev); return (ENXIO); } if_initname(ifp, device_get_name(sc->dev), sc->unit); ifp->if_softc = sc; ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN? ifp->if_capenable = ifp->if_capabilities; ifp->if_init = cpswp_init; ifp->if_start = cpswp_start; ifp->if_ioctl = cpswp_ioctl; ifp->if_snd.ifq_drv_maxlen = sc->swsc->tx.queue_slots; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); + /* FIXME: For now; Go and kidnap syscon from opp-table */ + /* ti,cpsw actually have an optional syscon reference but only for am33xx?? */ + opp_table = OF_finddevice("/opp-table"); + if (opp_table == -1) { + device_printf(dev, "Cant find /opp-table\n"); + cpswp_detach(dev); + return (ENXIO); + } + if (!OF_hasprop(opp_table, "syscon")) { + device_printf(dev, "/opp-table doesnt have required syscon property\n"); + cpswp_detach(dev); + return (ENXIO); + } + if (syscon_get_by_ofw_property(dev, opp_table, "syscon", &syscon) != 0) { + device_printf(dev, "Failed to get syscon\n"); + cpswp_detach(dev); + return (ENXIO); + } + /* Get high part of MAC address from control module (mac_id[0|1]_hi) */ - ti_scm_reg_read_4(SCM_MAC_ID0_HI + sc->unit * 8, ®); + reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_HI + sc->unit * 8); mac_addr[0] = reg & 0xFF; mac_addr[1] = (reg >> 8) & 0xFF; mac_addr[2] = (reg >> 16) & 0xFF; mac_addr[3] = (reg >> 24) & 0xFF; /* Get low part of MAC address from control module (mac_id[0|1]_lo) */ - ti_scm_reg_read_4(SCM_MAC_ID0_LO + sc->unit * 8, ®); + reg = SYSCON_READ_4(syscon, SCM_MAC_ID0_LO + sc->unit * 8); mac_addr[4] = reg & 0xFF; mac_addr[5] = (reg >> 8) & 0xFF; error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd, cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0); if (error) { device_printf(dev, "attaching PHYs failed\n"); cpswp_detach(dev); return (error); } sc->mii = device_get_softc(sc->miibus); /* Select PHY and enable interrupts */ cpsw_write_4(sc->swsc, sc->physel, MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F)); ether_ifattach(sc->ifp, mac_addr); callout_init(&sc->mii_callout, 0); return (0); } static int cpswp_detach(device_t dev) { struct cpswp_softc *sc; sc = device_get_softc(dev); CPSW_DEBUGF(sc->swsc, ("")); if (device_is_attached(dev)) { ether_ifdetach(sc->ifp); CPSW_PORT_LOCK(sc); cpswp_stop_locked(sc); CPSW_PORT_UNLOCK(sc); callout_drain(&sc->mii_callout); } bus_generic_detach(dev); if_free(sc->ifp); mtx_destroy(&sc->lock); return (0); } /* * * Init/Shutdown. * */ static int cpsw_ports_down(struct cpsw_softc *sc) { struct cpswp_softc *psc; struct ifnet *ifp1, *ifp2; if (!sc->dualemac) return (1); psc = device_get_softc(sc->port[0].dev); ifp1 = psc->ifp; psc = device_get_softc(sc->port[1].dev); ifp2 = psc->ifp; if ((ifp1->if_flags & IFF_UP) == 0 && (ifp2->if_flags & IFF_UP) == 0) return (1); return (0); } static void cpswp_init(void *arg) { struct cpswp_softc *sc = arg; CPSW_DEBUGF(sc->swsc, ("")); CPSW_PORT_LOCK(sc); cpswp_init_locked(arg); CPSW_PORT_UNLOCK(sc); } static void cpswp_init_locked(void *arg) { #ifdef CPSW_ETHERSWITCH int i; #endif struct cpswp_softc *sc = arg; struct ifnet *ifp; uint32_t reg; CPSW_DEBUGF(sc->swsc, ("")); CPSW_PORT_LOCK_ASSERT(sc); ifp = sc->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; getbinuptime(&sc->init_uptime); if (!sc->swsc->rx.running && !sc->swsc->tx.running) { /* Reset the controller. */ cpsw_reset(sc->swsc); cpsw_init(sc->swsc); } /* Set Slave Mapping. */ cpsw_write_4(sc->swsc, CPSW_SL_RX_PRI_MAP(sc->unit), 0x76543210); cpsw_write_4(sc->swsc, CPSW_PORT_P_TX_PRI_MAP(sc->unit + 1), 0x33221100); cpsw_write_4(sc->swsc, CPSW_SL_RX_MAXLEN(sc->unit), 0x5f2); /* Enable MAC RX/TX modules. */ /* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */ /* Huh? Docs call bit 0 "Loopback" some places, "FullDuplex" others. */ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); reg |= CPSW_SL_MACTL_GMII_ENABLE; cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); /* Initialize ALE: set port to forwarding, initialize addrs */ cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1), ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD); cpswp_ale_update_addresses(sc, 1); if (sc->swsc->dualemac) { /* Set Port VID. */ cpsw_write_4(sc->swsc, CPSW_PORT_P_VLAN(sc->unit + 1), sc->vlan & 0xfff); cpsw_ale_update_vlan_table(sc->swsc, sc->vlan, (1 << (sc->unit + 1)) | (1 << 0), /* Member list */ (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */ (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */ #ifdef CPSW_ETHERSWITCH for (i = 0; i < CPSW_VLANS; i++) { if (cpsw_vgroups[i].vid != -1) continue; cpsw_vgroups[i].vid = sc->vlan; break; } #endif } mii_mediachg(sc->mii); callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static int cpsw_shutdown(device_t dev) { struct cpsw_softc *sc; struct cpswp_softc *psc; int i; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("")); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } return (0); } static void cpsw_rx_teardown(struct cpsw_softc *sc) { int i = 0; CPSW_RX_LOCK(sc); CPSW_DEBUGF(sc, ("starting RX teardown")); sc->rx.teardown = 1; cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0); CPSW_RX_UNLOCK(sc); while (sc->rx.running) { if (++i > 10) { device_printf(sc->dev, "Unable to cleanly shutdown receiver\n"); return; } DELAY(200); } if (!sc->rx.running) CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i)); } static void cpsw_tx_teardown(struct cpsw_softc *sc) { int i = 0; CPSW_TX_LOCK(sc); CPSW_DEBUGF(sc, ("starting TX teardown")); /* Start the TX queue teardown if queue is not empty. */ if (STAILQ_FIRST(&sc->tx.active) != NULL) cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0); else sc->tx.teardown = 1; cpsw_tx_dequeue(sc); while (sc->tx.running && ++i < 10) { DELAY(200); cpsw_tx_dequeue(sc); } if (sc->tx.running) { device_printf(sc->dev, "Unable to cleanly shutdown transmitter\n"); } CPSW_DEBUGF(sc, ("finished TX teardown (%d retries, %d idle buffers)", i, sc->tx.active_queue_len)); CPSW_TX_UNLOCK(sc); } static void cpswp_stop_locked(struct cpswp_softc *sc) { struct ifnet *ifp; uint32_t reg; ifp = sc->ifp; CPSW_DEBUGF(sc->swsc, ("")); CPSW_PORT_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; /* Disable interface */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; ifp->if_drv_flags |= IFF_DRV_OACTIVE; /* Stop ticker */ callout_stop(&sc->mii_callout); /* Tear down the RX/TX queues. */ if (cpsw_ports_down(sc->swsc)) { cpsw_rx_teardown(sc->swsc); cpsw_tx_teardown(sc->swsc); } /* Stop MAC RX/TX modules. */ reg = cpsw_read_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit)); reg &= ~CPSW_SL_MACTL_GMII_ENABLE; cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg); if (cpsw_ports_down(sc->swsc)) { /* Capture stats before we reset controller. */ cpsw_stats_collect(sc->swsc); cpsw_reset(sc->swsc); cpsw_init(sc->swsc); } } /* * Suspend/Resume. */ static int cpsw_suspend(device_t dev) { struct cpsw_softc *sc; struct cpswp_softc *psc; int i; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("")); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } return (0); } static int cpsw_resume(device_t dev) { struct cpsw_softc *sc; sc = device_get_softc(dev); CPSW_DEBUGF(sc, ("UNIMPLEMENTED")); return (0); } /* * * IOCTL * */ static void cpsw_set_promisc(struct cpswp_softc *sc, int set) { uint32_t reg; /* * Enabling promiscuous mode requires ALE_BYPASS to be enabled. * That disables the ALE forwarding logic and causes every * packet to be sent only to the host port. In bypass mode, * the ALE processes host port transmit packets the same as in * normal mode. */ reg = cpsw_read_4(sc->swsc, CPSW_ALE_CONTROL); reg &= ~CPSW_ALE_CTL_BYPASS; if (set) reg |= CPSW_ALE_CTL_BYPASS; cpsw_write_4(sc->swsc, CPSW_ALE_CONTROL, reg); } static void cpsw_set_allmulti(struct cpswp_softc *sc, int set) { if (set) { printf("All-multicast mode unimplemented\n"); } } static int cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct cpswp_softc *sc; struct ifreq *ifr; int error; uint32_t changed; error = 0; sc = ifp->if_softc; ifr = (struct ifreq *)data; switch (command) { case SIOCSIFCAP: changed = ifp->if_capenable ^ ifr->ifr_reqcap; if (changed & IFCAP_HWCSUM) { if ((ifr->ifr_reqcap & changed) & IFCAP_HWCSUM) ifp->if_capenable |= IFCAP_HWCSUM; else ifp->if_capenable &= ~IFCAP_HWCSUM; } error = 0; break; case SIOCSIFFLAGS: CPSW_PORT_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { changed = ifp->if_flags ^ sc->if_flags; CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", changed)); if (changed & IFF_PROMISC) cpsw_set_promisc(sc, ifp->if_flags & IFF_PROMISC); if (changed & IFF_ALLMULTI) cpsw_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI); } else { CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: starting up")); cpswp_init_locked(sc); } } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: shutting down")); cpswp_stop_locked(sc); } sc->if_flags = ifp->if_flags; CPSW_PORT_UNLOCK(sc); break; case SIOCADDMULTI: cpswp_ale_update_addresses(sc, 0); break; case SIOCDELMULTI: /* Ugh. DELMULTI doesn't provide the specific address being removed, so the best we can do is remove everything and rebuild it all. */ cpswp_ale_update_addresses(sc, 1); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); } return (error); } /* * * MIIBUS * */ static int cpswp_miibus_ready(struct cpsw_softc *sc, uint32_t reg) { uint32_t r, retries = CPSW_MIIBUS_RETRIES; while (--retries) { r = cpsw_read_4(sc, reg); if ((r & MDIO_PHYACCESS_GO) == 0) return (1); DELAY(CPSW_MIIBUS_DELAY); } return (0); } static int cpswp_miibus_readreg(device_t dev, int phy, int reg) { struct cpswp_softc *sc; uint32_t cmd, r; sc = device_get_softc(dev); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to read\n"); return (0); } /* Set GO, reg, phy */ cmd = MDIO_PHYACCESS_GO | (reg & 0x1F) << 21 | (phy & 0x1F) << 16; cpsw_write_4(sc->swsc, sc->phyaccess, cmd); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during read\n"); return (0); } r = cpsw_read_4(sc->swsc, sc->phyaccess); if ((r & MDIO_PHYACCESS_ACK) == 0) { device_printf(dev, "Failed to read from PHY.\n"); r = 0; } return (r & 0xFFFF); } static int cpswp_miibus_writereg(device_t dev, int phy, int reg, int value) { struct cpswp_softc *sc; uint32_t cmd; sc = device_get_softc(dev); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO not ready to write\n"); return (0); } /* Set GO, WRITE, reg, phy, and value */ cmd = MDIO_PHYACCESS_GO | MDIO_PHYACCESS_WRITE | (reg & 0x1F) << 21 | (phy & 0x1F) << 16 | (value & 0xFFFF); cpsw_write_4(sc->swsc, sc->phyaccess, cmd); if (!cpswp_miibus_ready(sc->swsc, sc->phyaccess)) { device_printf(dev, "MDIO timed out during write\n"); return (0); } return (0); } static void cpswp_miibus_statchg(device_t dev) { struct cpswp_softc *sc; uint32_t mac_control, reg; sc = device_get_softc(dev); CPSW_DEBUGF(sc->swsc, ("")); reg = CPSW_SL_MACCONTROL(sc->unit); mac_control = cpsw_read_4(sc->swsc, reg); mac_control &= ~(CPSW_SL_MACTL_GIG | CPSW_SL_MACTL_IFCTL_A | CPSW_SL_MACTL_IFCTL_B | CPSW_SL_MACTL_FULLDUPLEX); switch(IFM_SUBTYPE(sc->mii->mii_media_active)) { case IFM_1000_SX: case IFM_1000_LX: case IFM_1000_CX: case IFM_1000_T: mac_control |= CPSW_SL_MACTL_GIG; break; case IFM_100_TX: mac_control |= CPSW_SL_MACTL_IFCTL_A; break; } if (sc->mii->mii_media_active & IFM_FDX) mac_control |= CPSW_SL_MACTL_FULLDUPLEX; cpsw_write_4(sc->swsc, reg, mac_control); } /* * * Transmit/Receive Packets. * */ static void cpsw_intr_rx(void *arg) { struct cpsw_softc *sc; struct ifnet *ifp; struct mbuf *received, *next; sc = (struct cpsw_softc *)arg; CPSW_RX_LOCK(sc); if (sc->rx.teardown) { sc->rx.running = 0; sc->rx.teardown = 0; cpsw_write_cp(sc, &sc->rx, 0xfffffffc); } received = cpsw_rx_dequeue(sc); cpsw_rx_enqueue(sc); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1); CPSW_RX_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; ifp = received->m_pkthdr.rcvif; (*ifp->if_input)(ifp, received); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); received = next; } } static struct mbuf * cpsw_rx_dequeue(struct cpsw_softc *sc) { int nsegs, port, removed; struct cpsw_cpdma_bd bd; struct cpsw_slot *last, *slot; struct cpswp_softc *psc; struct mbuf *m, *m0, *mb_head, *mb_tail; uint16_t m0_flags; nsegs = 0; m0 = NULL; last = NULL; mb_head = NULL; mb_tail = NULL; removed = 0; /* Pull completed packets off hardware RX queue. */ while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) { cpsw_cpdma_read_bd(sc, slot, &bd); /* * Stop on packets still in use by hardware, but do not stop * on packets with the teardown complete flag, they will be * discarded later. */ if ((bd.flags & (CPDMA_BD_OWNER | CPDMA_BD_TDOWNCMPLT)) == CPDMA_BD_OWNER) break; last = slot; ++removed; STAILQ_REMOVE_HEAD(&sc->rx.active, next); STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next); bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m = slot->mbuf; slot->mbuf = NULL; if (bd.flags & CPDMA_BD_TDOWNCMPLT) { CPSW_DEBUGF(sc, ("RX teardown is complete")); m_freem(m); sc->rx.running = 0; sc->rx.teardown = 0; break; } port = (bd.flags & CPDMA_BD_PORT_MASK) - 1; KASSERT(port >= 0 && port <= 1, ("patcket received with invalid port: %d", port)); psc = device_get_softc(sc->port[port].dev); /* Set up mbuf */ m->m_data += bd.bufoff; m->m_len = bd.buflen; if (bd.flags & CPDMA_BD_SOP) { m->m_pkthdr.len = bd.pktlen; m->m_pkthdr.rcvif = psc->ifp; m->m_flags |= M_PKTHDR; m0_flags = bd.flags; m0 = m; } nsegs++; m->m_next = NULL; m->m_nextpkt = NULL; if (bd.flags & CPDMA_BD_EOP && m0 != NULL) { if (m0_flags & CPDMA_BD_PASS_CRC) m_adj(m0, -ETHER_CRC_LEN); m0_flags = 0; m0 = NULL; if (nsegs > sc->rx.longest_chain) sc->rx.longest_chain = nsegs; nsegs = 0; } if ((psc->ifp->if_capenable & IFCAP_RXCSUM) != 0) { /* check for valid CRC by looking into pkt_err[5:4] */ if ((bd.flags & (CPDMA_BD_SOP | CPDMA_BD_PKT_ERR_MASK)) == CPDMA_BD_SOP) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; m->m_pkthdr.csum_flags |= CSUM_IP_VALID; m->m_pkthdr.csum_data = 0xffff; } } if (STAILQ_FIRST(&sc->rx.active) != NULL && (bd.flags & (CPDMA_BD_EOP | CPDMA_BD_EOQ)) == (CPDMA_BD_EOP | CPDMA_BD_EOQ)) { cpsw_write_hdp_slot(sc, &sc->rx, STAILQ_FIRST(&sc->rx.active)); sc->rx.queue_restart++; } /* Add mbuf to packet list to be returned. */ if (mb_tail != NULL && (bd.flags & CPDMA_BD_SOP)) { mb_tail->m_nextpkt = m; } else if (mb_tail != NULL) { mb_tail->m_next = m; } else if (mb_tail == NULL && (bd.flags & CPDMA_BD_SOP) == 0) { if (bootverbose) printf( "%s: %s: discanding fragment packet w/o header\n", __func__, psc->ifp->if_xname); m_freem(m); continue; } else { mb_head = m; } mb_tail = m; } if (removed != 0) { cpsw_write_cp_slot(sc, &sc->rx, last); sc->rx.queue_removes += removed; sc->rx.avail_queue_len += removed; sc->rx.active_queue_len -= removed; if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len) sc->rx.max_avail_queue_len = sc->rx.avail_queue_len; CPSW_DEBUGF(sc, ("Removed %d received packet(s) from RX queue", removed)); } return (mb_head); } static void cpsw_rx_enqueue(struct cpsw_softc *sc) { bus_dma_segment_t seg[1]; struct cpsw_cpdma_bd bd; struct cpsw_slot *first_new_slot, *last_old_slot, *next, *slot; int error, nsegs, added = 0; /* Register new mbufs with hardware. */ first_new_slot = NULL; last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next); while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) { if (first_new_slot == NULL) first_new_slot = slot; if (slot->mbuf == NULL) { slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (slot->mbuf == NULL) { device_printf(sc->dev, "Unable to fill RX queue\n"); break; } slot->mbuf->m_len = slot->mbuf->m_pkthdr.len = slot->mbuf->m_ext.ext_size; } error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap, slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT); KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs)); KASSERT(error == 0, ("DMA error (error=%d)", error)); if (error != 0 || nsegs != 1) { device_printf(sc->dev, "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n", __func__, nsegs, error); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD); /* Create and submit new rx descriptor. */ if ((next = STAILQ_NEXT(slot, next)) != NULL) bd.next = cpsw_cpdma_bd_paddr(sc, next); else bd.next = 0; bd.bufptr = seg->ds_addr; bd.bufoff = 0; bd.buflen = MCLBYTES - 1; bd.pktlen = bd.buflen; bd.flags = CPDMA_BD_OWNER; cpsw_cpdma_write_bd(sc, slot, &bd); ++added; STAILQ_REMOVE_HEAD(&sc->rx.avail, next); STAILQ_INSERT_TAIL(&sc->rx.active, slot, next); } if (added == 0 || first_new_slot == NULL) return; CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added)); /* Link new entries to hardware RX queue. */ if (last_old_slot == NULL) { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot); } else { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot); } sc->rx.queue_adds += added; sc->rx.avail_queue_len -= added; sc->rx.active_queue_len += added; cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), added); if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) sc->rx.max_active_queue_len = sc->rx.active_queue_len; } static void cpswp_start(struct ifnet *ifp) { struct cpswp_softc *sc; sc = ifp->if_softc; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->swsc->tx.running == 0) { return; } CPSW_TX_LOCK(sc->swsc); cpswp_tx_enqueue(sc); cpsw_tx_dequeue(sc->swsc); CPSW_TX_UNLOCK(sc->swsc); } static void cpsw_intr_tx(void *arg) { struct cpsw_softc *sc; sc = (struct cpsw_softc *)arg; CPSW_TX_LOCK(sc); if (cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)) == 0xfffffffc) cpsw_write_cp(sc, &sc->tx, 0xfffffffc); cpsw_tx_dequeue(sc); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 2); CPSW_TX_UNLOCK(sc); } static void cpswp_tx_enqueue(struct cpswp_softc *sc) { bus_dma_segment_t segs[CPSW_TXFRAGS]; struct cpsw_cpdma_bd bd; struct cpsw_slot *first_new_slot, *last, *last_old_slot, *next, *slot; struct mbuf *m0; int error, nsegs, seg, added = 0, padlen; /* Pull pending packets from IF queue and prep them for DMA. */ last = NULL; first_new_slot = NULL; last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next); while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) { IF_DEQUEUE(&sc->ifp->if_snd, m0); if (m0 == NULL) break; slot->mbuf = m0; padlen = ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len; if (padlen < 0) padlen = 0; else if (padlen > 0) m_append(slot->mbuf, padlen, sc->swsc->nullpad); /* Create mapping in DMA memory */ error = bus_dmamap_load_mbuf_sg(sc->swsc->mbuf_dtag, slot->dmamap, slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT); /* If the packet is too fragmented, try to simplify. */ if (error == EFBIG || (error == 0 && nsegs > sc->swsc->tx.avail_queue_len)) { bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); m0 = m_defrag(slot->mbuf, M_NOWAIT); if (m0 == NULL) { device_printf(sc->dev, "Can't defragment packet; dropping\n"); m_freem(slot->mbuf); } else { CPSW_DEBUGF(sc->swsc, ("Requeueing defragmented packet")); IF_PREPEND(&sc->ifp->if_snd, m0); } slot->mbuf = NULL; continue; } if (error != 0) { device_printf(sc->dev, "%s: Can't setup DMA (error=%d), dropping packet\n", __func__, error); bus_dmamap_unload(sc->swsc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; break; } bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREWRITE); CPSW_DEBUGF(sc->swsc, ("Queueing TX packet: %d segments + %d pad bytes", nsegs, padlen)); if (first_new_slot == NULL) first_new_slot = slot; /* Link from the previous descriptor. */ if (last != NULL) cpsw_cpdma_write_bd_next(sc->swsc, last, slot); slot->ifp = sc->ifp; /* If there is only one segment, the for() loop * gets skipped and the single buffer gets set up * as both SOP and EOP. */ if (nsegs > 1) { next = STAILQ_NEXT(slot, next); bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next); } else bd.next = 0; /* Start by setting up the first buffer. */ bd.bufptr = segs[0].ds_addr; bd.bufoff = 0; bd.buflen = segs[0].ds_len; bd.pktlen = m_length(slot->mbuf, NULL); bd.flags = CPDMA_BD_SOP | CPDMA_BD_OWNER; if (sc->swsc->dualemac) { bd.flags |= CPDMA_BD_TO_PORT; bd.flags |= ((sc->unit + 1) & CPDMA_BD_PORT_MASK); } for (seg = 1; seg < nsegs; ++seg) { /* Save the previous buffer (which isn't EOP) */ cpsw_cpdma_write_bd(sc->swsc, slot, &bd); STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next); slot = STAILQ_FIRST(&sc->swsc->tx.avail); /* Setup next buffer (which isn't SOP) */ if (nsegs > seg + 1) { next = STAILQ_NEXT(slot, next); bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next); } else bd.next = 0; bd.bufptr = segs[seg].ds_addr; bd.bufoff = 0; bd.buflen = segs[seg].ds_len; bd.pktlen = 0; bd.flags = CPDMA_BD_OWNER; } /* Save the final buffer. */ bd.flags |= CPDMA_BD_EOP; cpsw_cpdma_write_bd(sc->swsc, slot, &bd); STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next); STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next); last = slot; added += nsegs; if (nsegs > sc->swsc->tx.longest_chain) sc->swsc->tx.longest_chain = nsegs; BPF_MTAP(sc->ifp, m0); } if (first_new_slot == NULL) return; /* Attach the list of new buffers to the hardware TX queue. */ if (last_old_slot != NULL && (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) & CPDMA_BD_EOQ) == 0) { /* Add buffers to end of current queue. */ cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot, first_new_slot); } else { /* Start a fresh queue. */ cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot); } sc->swsc->tx.queue_adds += added; sc->swsc->tx.avail_queue_len -= added; sc->swsc->tx.active_queue_len += added; if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) { sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len; } CPSW_DEBUGF(sc->swsc, ("Queued %d TX packet(s)", added)); } static int cpsw_tx_dequeue(struct cpsw_softc *sc) { struct cpsw_slot *slot, *last_removed_slot = NULL; struct cpsw_cpdma_bd bd; uint32_t flags, removed = 0; /* Pull completed buffers off the hardware TX queue. */ slot = STAILQ_FIRST(&sc->tx.active); while (slot != NULL) { flags = cpsw_cpdma_read_bd_flags(sc, slot); /* TearDown complete is only marked on the SOP for the packet. */ if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) == (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) { sc->tx.teardown = 1; } if ((flags & (CPDMA_BD_SOP | CPDMA_BD_OWNER)) == (CPDMA_BD_SOP | CPDMA_BD_OWNER) && sc->tx.teardown == 0) break; /* Hardware is still using this packet. */ bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap); m_freem(slot->mbuf); slot->mbuf = NULL; if (slot->ifp) { if (sc->tx.teardown == 0) if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1); else if_inc_counter(slot->ifp, IFCOUNTER_OQDROPS, 1); } /* Dequeue any additional buffers used by this packet. */ while (slot != NULL && slot->mbuf == NULL) { STAILQ_REMOVE_HEAD(&sc->tx.active, next); STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next); ++removed; last_removed_slot = slot; slot = STAILQ_FIRST(&sc->tx.active); } cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot); /* Restart the TX queue if necessary. */ cpsw_cpdma_read_bd(sc, last_removed_slot, &bd); if (slot != NULL && bd.next != 0 && (bd.flags & (CPDMA_BD_EOP | CPDMA_BD_OWNER | CPDMA_BD_EOQ)) == (CPDMA_BD_EOP | CPDMA_BD_EOQ)) { cpsw_write_hdp_slot(sc, &sc->tx, slot); sc->tx.queue_restart++; break; } } if (removed != 0) { sc->tx.queue_removes += removed; sc->tx.active_queue_len -= removed; sc->tx.avail_queue_len += removed; if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len) sc->tx.max_avail_queue_len = sc->tx.avail_queue_len; CPSW_DEBUGF(sc, ("TX removed %d completed packet(s)", removed)); } if (sc->tx.teardown && STAILQ_EMPTY(&sc->tx.active)) { CPSW_DEBUGF(sc, ("TX teardown is complete")); sc->tx.teardown = 0; sc->tx.running = 0; } return (removed); } /* * * Miscellaneous interrupts. * */ static void cpsw_intr_rx_thresh(void *arg) { struct cpsw_softc *sc; struct ifnet *ifp; struct mbuf *received, *next; sc = (struct cpsw_softc *)arg; CPSW_RX_LOCK(sc); received = cpsw_rx_dequeue(sc); cpsw_rx_enqueue(sc); cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0); CPSW_RX_UNLOCK(sc); while (received != NULL) { next = received->m_nextpkt; received->m_nextpkt = NULL; ifp = received->m_pkthdr.rcvif; (*ifp->if_input)(ifp, received); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); received = next; } } static void cpsw_intr_misc_host_error(struct cpsw_softc *sc) { uint32_t intstat; uint32_t dmastat; int txerr, rxerr, txchan, rxchan; printf("\n\n"); device_printf(sc->dev, "HOST ERROR: PROGRAMMING ERROR DETECTED BY HARDWARE\n"); printf("\n\n"); intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED); device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat); dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS); device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat); txerr = (dmastat >> 20) & 15; txchan = (dmastat >> 16) & 7; rxerr = (dmastat >> 12) & 15; rxchan = (dmastat >> 8) & 7; switch (txerr) { case 0: break; case 1: printf("SOP error on TX channel %d\n", txchan); break; case 2: printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan); break; case 3: printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan); break; case 4: printf("Zero Buffer Pointer on TX channel %d\n", txchan); break; case 5: printf("Zero Buffer Length on TX channel %d\n", txchan); break; case 6: printf("Packet length error on TX channel %d\n", txchan); break; default: printf("Unknown error on TX channel %d\n", txchan); break; } if (txerr != 0) { printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan))); printf("CPSW_CPDMA_TX%d_CP=0x%x\n", txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan))); cpsw_dump_queue(sc, &sc->tx.active); } switch (rxerr) { case 0: break; case 2: printf("Ownership bit not set on RX channel %d\n", rxchan); break; case 4: printf("Zero Buffer Pointer on RX channel %d\n", rxchan); break; case 5: printf("Zero Buffer Length on RX channel %d\n", rxchan); break; case 6: printf("Buffer offset too big on RX channel %d\n", rxchan); break; default: printf("Unknown RX error on RX channel %d\n", rxchan); break; } if (rxerr != 0) { printf("CPSW_CPDMA_RX%d_HDP=0x%x\n", rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan))); printf("CPSW_CPDMA_RX%d_CP=0x%x\n", rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan))); cpsw_dump_queue(sc, &sc->rx.active); } printf("\nALE Table\n"); cpsw_ale_dump_table(sc); // XXX do something useful here?? panic("CPSW HOST ERROR INTERRUPT"); // Suppress this interrupt in the future. cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat); printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n"); // The watchdog will probably reset the controller // in a little while. It will probably fail again. } static void cpsw_intr_misc(void *arg) { struct cpsw_softc *sc = arg; uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0)); if (stat & CPSW_WR_C_MISC_EVNT_PEND) CPSW_DEBUGF(sc, ("Time sync event interrupt unimplemented")); if (stat & CPSW_WR_C_MISC_STAT_PEND) cpsw_stats_collect(sc); if (stat & CPSW_WR_C_MISC_HOST_PEND) cpsw_intr_misc_host_error(sc); if (stat & CPSW_WR_C_MISC_MDIOLINK) { cpsw_write_4(sc, MDIOLINKINTMASKED, cpsw_read_4(sc, MDIOLINKINTMASKED)); } if (stat & CPSW_WR_C_MISC_MDIOUSER) { CPSW_DEBUGF(sc, ("MDIO operation completed interrupt unimplemented")); } cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3); } /* * * Periodic Checks and Watchdog. * */ static void cpswp_tick(void *msc) { struct cpswp_softc *sc = msc; /* Check for media type change */ mii_tick(sc->mii); if (sc->media_status != sc->mii->mii_media.ifm_media) { printf("%s: media type changed (ifm_media=%x)\n", __func__, sc->mii->mii_media.ifm_media); cpswp_ifmedia_upd(sc->ifp); } /* Schedule another timeout one second from now */ callout_reset(&sc->mii_callout, hz, cpswp_tick, sc); } static void cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct cpswp_softc *sc; struct mii_data *mii; sc = ifp->if_softc; CPSW_DEBUGF(sc->swsc, ("")); CPSW_PORT_LOCK(sc); mii = sc->mii; mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; CPSW_PORT_UNLOCK(sc); } static int cpswp_ifmedia_upd(struct ifnet *ifp) { struct cpswp_softc *sc; sc = ifp->if_softc; CPSW_DEBUGF(sc->swsc, ("")); CPSW_PORT_LOCK(sc); mii_mediachg(sc->mii); sc->media_status = sc->mii->mii_media.ifm_media; CPSW_PORT_UNLOCK(sc); return (0); } static void cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc) { struct cpswp_softc *psc; int i; cpsw_debugf_head("CPSW watchdog"); device_printf(sc->dev, "watchdog timeout\n"); printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", 0, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(0))); printf("CPSW_CPDMA_TX%d_CP=0x%x\n", 0, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0))); cpsw_dump_queue(sc, &sc->tx.active); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; psc = device_get_softc(sc->port[i].dev); CPSW_PORT_LOCK(psc); cpswp_stop_locked(psc); CPSW_PORT_UNLOCK(psc); } } static void cpsw_tx_watchdog(void *msc) { struct cpsw_softc *sc; sc = msc; CPSW_TX_LOCK(sc); if (sc->tx.active_queue_len == 0 || !sc->tx.running) { sc->watchdog.timer = 0; /* Nothing to do. */ } else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) { sc->watchdog.timer = 0; /* Stuff done while we weren't looking. */ } else if (cpsw_tx_dequeue(sc) > 0) { sc->watchdog.timer = 0; /* We just did something. */ } else { /* There was something to do but it didn't get done. */ ++sc->watchdog.timer; if (sc->watchdog.timer > 5) { sc->watchdog.timer = 0; ++sc->watchdog.resets; cpsw_tx_watchdog_full_reset(sc); } } sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes; CPSW_TX_UNLOCK(sc); /* Schedule another timeout one second from now */ callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc); } /* * * ALE support routines. * */ static void cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023); ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0); ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1); ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2); } static void cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry) { cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]); cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]); cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]); cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023)); } static void cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; /* First four entries are link address and broadcast. */ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if ((ALE_TYPE(ale_entry) == ALE_TYPE_ADDR || ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) && ALE_MCAST(ale_entry) == 1) { /* MCast link addr */ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); } } } static int cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, int vlan, uint8_t *mac) { int free_index = -1, matching_index = -1, i; uint32_t ale_entry[3], ale_type; /* Find a matching entry or a free entry. */ for (i = 10; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); /* Entry Type[61:60] is 0 for free entry */ if (free_index < 0 && ALE_TYPE(ale_entry) == 0) free_index = i; if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) && (((ale_entry[1] >> 0) & 0xFF) == mac[1]) && (((ale_entry[0] >>24) & 0xFF) == mac[2]) && (((ale_entry[0] >>16) & 0xFF) == mac[3]) && (((ale_entry[0] >> 8) & 0xFF) == mac[4]) && (((ale_entry[0] >> 0) & 0xFF) == mac[5])) { matching_index = i; break; } } if (matching_index < 0) { if (free_index < 0) return (ENOMEM); i = free_index; } if (vlan != -1) ale_type = ALE_TYPE_VLAN_ADDR << 28 | vlan << 16; else ale_type = ALE_TYPE_ADDR << 28; /* Set MAC address */ ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = mac[0] << 8 | mac[1]; /* Entry type[61:60] and Mcast fwd state[63:62] is fw(3). */ ale_entry[1] |= ALE_MCAST_FWD | ale_type; /* Set portmask [68:66] */ ale_entry[2] = (portmap & 7) << 2; cpsw_ale_write_entry(sc, i, ale_entry); return 0; } static void cpsw_ale_dump_table(struct cpsw_softc *sc) { int i; uint32_t ale_entry[3]; for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); switch (ALE_TYPE(ale_entry)) { case ALE_TYPE_VLAN: printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], ale_entry[1], ale_entry[0]); printf("type: %u ", ALE_TYPE(ale_entry)); printf("vlan: %u ", ALE_VLAN(ale_entry)); printf("untag: %u ", ALE_VLAN_UNTAG(ale_entry)); printf("reg flood: %u ", ALE_VLAN_REGFLOOD(ale_entry)); printf("unreg flood: %u ", ALE_VLAN_UNREGFLOOD(ale_entry)); printf("members: %u ", ALE_VLAN_MEMBERS(ale_entry)); printf("\n"); break; case ALE_TYPE_ADDR: case ALE_TYPE_VLAN_ADDR: printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[2], ale_entry[1], ale_entry[0]); printf("type: %u ", ALE_TYPE(ale_entry)); printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ", (ale_entry[1] >> 8) & 0xFF, (ale_entry[1] >> 0) & 0xFF, (ale_entry[0] >>24) & 0xFF, (ale_entry[0] >>16) & 0xFF, (ale_entry[0] >> 8) & 0xFF, (ale_entry[0] >> 0) & 0xFF); printf(ALE_MCAST(ale_entry) ? "mcast " : "ucast "); if (ALE_TYPE(ale_entry) == ALE_TYPE_VLAN_ADDR) printf("vlan: %u ", ALE_VLAN(ale_entry)); printf("port: %u ", ALE_PORTS(ale_entry)); printf("\n"); break; } } printf("\n"); } static u_int cpswp_set_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { struct cpswp_softc *sc = arg; uint32_t portmask; if (sc->swsc->dualemac) portmask = 1 << (sc->unit + 1) | 1 << 0; else portmask = 7; cpsw_ale_mc_entry_set(sc->swsc, portmask, sc->vlan, LLADDR(sdl)); return (1); } static int cpswp_ale_update_addresses(struct cpswp_softc *sc, int purge) { uint8_t *mac; uint32_t ale_entry[3], ale_type, portmask; if (sc->swsc->dualemac) { ale_type = ALE_TYPE_VLAN_ADDR << 28 | sc->vlan << 16; portmask = 1 << (sc->unit + 1) | 1 << 0; } else { ale_type = ALE_TYPE_ADDR << 28; portmask = 7; } /* * Route incoming packets for our MAC address to Port 0 (host). * For simplicity, keep this entry at table index 0 for port 1 and * at index 2 for port 2 in the ALE. */ mac = LLADDR((struct sockaddr_dl *)sc->ifp->if_addr->ifa_addr); ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; ale_entry[1] = ale_type | mac[0] << 8 | mac[1]; /* addr entry + mac */ ale_entry[2] = 0; /* port = 0 */ cpsw_ale_write_entry(sc->swsc, 0 + 2 * sc->unit, ale_entry); /* Set outgoing MAC Address for slave port. */ cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_HI(sc->unit + 1), mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]); cpsw_write_4(sc->swsc, CPSW_PORT_P_SA_LO(sc->unit + 1), mac[5] << 8 | mac[4]); /* Keep the broadcast address at table entry 1 (or 3). */ ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */ /* ALE_MCAST_FWD, Addr type, upper 16 bits of Mac */ ale_entry[1] = ALE_MCAST_FWD | ale_type | 0xffff; ale_entry[2] = portmask << 2; cpsw_ale_write_entry(sc->swsc, 1 + 2 * sc->unit, ale_entry); /* SIOCDELMULTI doesn't specify the particular address being removed, so we have to remove all and rebuild. */ if (purge) cpsw_ale_remove_all_mc_entries(sc->swsc); /* Set other multicast addrs desired. */ if_foreach_llmaddr(sc->ifp, cpswp_set_maddr, sc); return (0); } static int cpsw_ale_update_vlan_table(struct cpsw_softc *sc, int vlan, int ports, int untag, int mcregflood, int mcunregflood) { int free_index, i, matching_index; uint32_t ale_entry[3]; free_index = matching_index = -1; /* Find a matching entry or a free entry. */ for (i = 5; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); /* Entry Type[61:60] is 0 for free entry */ if (free_index < 0 && ALE_TYPE(ale_entry) == 0) free_index = i; if (ALE_VLAN(ale_entry) == vlan) { matching_index = i; break; } } if (matching_index < 0) { if (free_index < 0) return (-1); i = free_index; } ale_entry[0] = (untag & 7) << 24 | (mcregflood & 7) << 16 | (mcunregflood & 7) << 8 | (ports & 7); ale_entry[1] = ALE_TYPE_VLAN << 28 | vlan << 16; ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); return (0); } /* * * Statistics and Sysctls. * */ #if 0 static void cpsw_stats_dump(struct cpsw_softc *sc) { int i; uint32_t r; for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); CPSW_DEBUGF(sc, ("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid, (intmax_t)sc->shadow_stats[i], r, (intmax_t)sc->shadow_stats[i] + r)); } } #endif static void cpsw_stats_collect(struct cpsw_softc *sc) { int i; uint32_t r; CPSW_DEBUGF(sc, ("Controller shadow statistics updated.")); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { r = cpsw_read_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg); sc->shadow_stats[i] += r; cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, r); } } static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct cpsw_stat *stat; uint64_t result; sc = (struct cpsw_softc *)arg1; stat = &cpsw_stat_sysctls[oidp->oid_number]; result = sc->shadow_stats[oidp->oid_number]; result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg); return (sysctl_handle_64(oidp, &result, 0, req)); } static int cpsw_stat_attached(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *sc; struct bintime t; unsigned result; sc = (struct cpsw_softc *)arg1; getbinuptime(&t); bintime_sub(&t, &sc->attach_uptime); result = t.sec; return (sysctl_handle_int(oidp, &result, 0, req)); } static int cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS) { int error; struct cpsw_softc *sc; uint32_t ctrl, intr_per_ms; sc = (struct cpsw_softc *)arg1; error = sysctl_handle_int(oidp, &sc->coal_us, 0, req); if (error != 0 || req->newptr == NULL) return (error); ctrl = cpsw_read_4(sc, CPSW_WR_INT_CONTROL); ctrl &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK); if (sc->coal_us == 0) { /* Disable the interrupt pace hardware. */ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl); cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), 0); cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), 0); return (0); } if (sc->coal_us > CPSW_WR_C_IMAX_US_MAX) sc->coal_us = CPSW_WR_C_IMAX_US_MAX; if (sc->coal_us < CPSW_WR_C_IMAX_US_MIN) sc->coal_us = CPSW_WR_C_IMAX_US_MIN; intr_per_ms = 1000 / sc->coal_us; /* Just to make sure... */ if (intr_per_ms > CPSW_WR_C_IMAX_MAX) intr_per_ms = CPSW_WR_C_IMAX_MAX; if (intr_per_ms < CPSW_WR_C_IMAX_MIN) intr_per_ms = CPSW_WR_C_IMAX_MIN; /* Set the prescale to produce 4us pulses from the 125 Mhz clock. */ ctrl |= (125 * 4) & CPSW_WR_INT_PRESCALE_MASK; /* Enable the interrupt pace hardware. */ cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), intr_per_ms); cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), intr_per_ms); ctrl |= CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE; cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl); return (0); } static int cpsw_stat_uptime(SYSCTL_HANDLER_ARGS) { struct cpsw_softc *swsc; struct cpswp_softc *sc; struct bintime t; unsigned result; swsc = arg1; sc = device_get_softc(swsc->port[arg2].dev); if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { getbinuptime(&t); bintime_sub(&t, &sc->init_uptime); result = t.sec; } else result = 0; return (sysctl_handle_int(oidp, &result, 0, req)); } static void cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_queue *queue) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers", CTLFLAG_RD, &queue->queue_slots, 0, "Total buffers currently assigned to this queue"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers", CTLFLAG_RD, &queue->active_queue_len, 0, "Buffers currently registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers", CTLFLAG_RD, &queue->max_active_queue_len, 0, "Max value of activeBuffers since last driver reset"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers", CTLFLAG_RD, &queue->avail_queue_len, 0, "Buffers allocated to this queue but not currently " "registered with hardware controller"); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers", CTLFLAG_RD, &queue->max_avail_queue_len, 0, "Max value of availBuffers since last driver reset"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued", CTLFLAG_RD, &queue->queue_adds, 0, "Total buffers added to queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued", CTLFLAG_RD, &queue->queue_removes, 0, "Total buffers removed from queue"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "queueRestart", CTLFLAG_RD, &queue->queue_restart, 0, "Total times the queue has been restarted"); SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain", CTLFLAG_RD, &queue->longest_chain, 0, "Max buffers used for a single packet"); } static void cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_softc *sc) { struct sysctl_oid_list *parent; parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets", CTLFLAG_RD, &sc->watchdog.resets, 0, "Total number of watchdog resets"); } static void cpsw_add_sysctls(struct cpsw_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *stats_node, *queue_node, *node; struct sysctl_oid_list *parent, *stats_parent, *queue_parent; struct sysctl_oid_list *ports_parent, *port_parent; char port[16]; int i; ctx = device_get_sysctl_ctx(sc->dev); parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug", CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages"); SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, cpsw_stat_attached, "IU", "Time since driver attach"); SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "intr_coalesce_us", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, cpsw_intr_coalesce, "IU", "minimum time between interrupts"); node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Ports Statistics"); ports_parent = SYSCTL_CHILDREN(node); for (i = 0; i < CPSW_PORTS; i++) { if (!sc->dualemac && i != sc->active_slave) continue; port[0] = '0' + i; port[1] = '\0'; node = SYSCTL_ADD_NODE(ctx, ports_parent, OID_AUTO, port, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Port Statistics"); port_parent = SYSCTL_CHILDREN(node); SYSCTL_ADD_PROC(ctx, port_parent, OID_AUTO, "uptime", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i, cpsw_stat_uptime, "IU", "Seconds since driver init"); } stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Statistics"); stats_parent = SYSCTL_CHILDREN(stats_node); for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) { SYSCTL_ADD_PROC(ctx, stats_parent, i, cpsw_stat_sysctls[i].oid, CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, cpsw_stats_sysctl, "IU", cpsw_stat_sysctls[i].oid); } queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "CPSW Queue Statistics"); queue_parent = SYSCTL_CHILDREN(queue_node); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->tx); node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX Queue Statistics"); cpsw_add_queue_sysctls(ctx, node, &sc->rx); node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Watchdog Statistics"); cpsw_add_watchdog_sysctls(ctx, node, sc); } #ifdef CPSW_ETHERSWITCH static etherswitch_info_t etherswitch_info = { .es_nports = CPSW_PORTS + 1, .es_nvlangroups = CPSW_VLANS, .es_name = "TI Common Platform Ethernet Switch (CPSW)", .es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q, }; static etherswitch_info_t * cpsw_getinfo(device_t dev) { return (ðerswitch_info); } static int cpsw_getport(device_t dev, etherswitch_port_t *p) { int err; struct cpsw_softc *sc; struct cpswp_softc *psc; struct ifmediareq *ifmr; uint32_t reg; if (p->es_port < 0 || p->es_port > CPSW_PORTS) return (ENXIO); err = 0; sc = device_get_softc(dev); if (p->es_port == CPSW_CPU_PORT) { p->es_flags |= ETHERSWITCH_PORT_CPU; ifmr = &p->es_ifmr; ifmr->ifm_current = ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX; ifmr->ifm_mask = 0; ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; ifmr->ifm_count = 0; } else { psc = device_get_softc(sc->port[p->es_port - 1].dev); err = ifmedia_ioctl(psc->ifp, &p->es_ifr, &psc->mii->mii_media, SIOCGIFMEDIA); } reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port)); p->es_pvid = reg & ETHERSWITCH_VID_MASK; reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port)); if (reg & ALE_PORTCTL_DROP_UNTAGGED) p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED; if (reg & ALE_PORTCTL_INGRESS) p->es_flags |= ETHERSWITCH_PORT_INGRESS; return (err); } static int cpsw_setport(device_t dev, etherswitch_port_t *p) { struct cpsw_softc *sc; struct cpswp_softc *psc; struct ifmedia *ifm; uint32_t reg; if (p->es_port < 0 || p->es_port > CPSW_PORTS) return (ENXIO); sc = device_get_softc(dev); if (p->es_pvid != 0) { cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port), p->es_pvid & ETHERSWITCH_VID_MASK); } reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port)); if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED) reg |= ALE_PORTCTL_DROP_UNTAGGED; else reg &= ~ALE_PORTCTL_DROP_UNTAGGED; if (p->es_flags & ETHERSWITCH_PORT_INGRESS) reg |= ALE_PORTCTL_INGRESS; else reg &= ~ALE_PORTCTL_INGRESS; cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg); /* CPU port does not allow media settings. */ if (p->es_port == CPSW_CPU_PORT) return (0); psc = device_get_softc(sc->port[p->es_port - 1].dev); ifm = &psc->mii->mii_media; return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); } static int cpsw_getconf(device_t dev, etherswitch_conf_t *conf) { /* Return the VLAN mode. */ conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; return (0); } static int cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) { int i, vid; uint32_t ale_entry[3]; struct cpsw_softc *sc; sc = device_get_softc(dev); if (vg->es_vlangroup >= CPSW_VLANS) return (EINVAL); vg->es_vid = 0; vid = cpsw_vgroups[vg->es_vlangroup].vid; if (vid == -1) return (0); for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN) continue; if (vid != ALE_VLAN(ale_entry)) continue; vg->es_fid = 0; vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID; vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry); vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry); } return (0); } static void cpsw_remove_vlan(struct cpsw_softc *sc, int vlan) { int i; uint32_t ale_entry[3]; for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) { cpsw_ale_read_entry(sc, i, ale_entry); if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN) continue; if (vlan != ALE_VLAN(ale_entry)) continue; ale_entry[0] = ale_entry[1] = ale_entry[2] = 0; cpsw_ale_write_entry(sc, i, ale_entry); break; } } static int cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) { int i; struct cpsw_softc *sc; sc = device_get_softc(dev); for (i = 0; i < CPSW_VLANS; i++) { /* Is this Vlan ID in use by another vlangroup ? */ if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid) return (EINVAL); } if (vg->es_vid == 0) { if (cpsw_vgroups[vg->es_vlangroup].vid == -1) return (0); cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid); cpsw_vgroups[vg->es_vlangroup].vid = -1; vg->es_untagged_ports = 0; vg->es_member_ports = 0; vg->es_vid = 0; return (0); } vg->es_vid &= ETHERSWITCH_VID_MASK; vg->es_member_ports &= CPSW_PORTS_MASK; vg->es_untagged_ports &= CPSW_PORTS_MASK; if (cpsw_vgroups[vg->es_vlangroup].vid != -1 && cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid) return (EINVAL); cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid; cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports, vg->es_untagged_ports, vg->es_member_ports, 0); return (0); } static int cpsw_readreg(device_t dev, int addr) { /* Not supported. */ return (0); } static int cpsw_writereg(device_t dev, int addr, int value) { /* Not supported. */ return (0); } static int cpsw_readphy(device_t dev, int phy, int reg) { /* Not supported. */ return (0); } static int cpsw_writephy(device_t dev, int phy, int reg, int data) { /* Not supported. */ return (0); } #endif Index: head/sys/arm/ti/files.ti =================================================================== --- head/sys/arm/ti/files.ti (revision 363699) +++ head/sys/arm/ti/files.ti (revision 363700) @@ -1,23 +1,34 @@ #$FreeBSD$ arm/ti/ti_cpuid.c standard -arm/ti/ti_hwmods.c standard arm/ti/ti_machdep.c standard arm/ti/ti_prcm.c standard +arm/ti/ti_omap4_cm.c standard arm/ti/ti_scm.c standard +arm/ti/ti_scm_syscon.c standard arm/ti/ti_pinmux.c standard dev/mbox/mbox_if.m optional ti_mbox arm/ti/ti_mbox.c optional ti_mbox arm/ti/ti_pruss.c optional ti_pruss +arm/ti/ti_prm.c optional ti_pruss arm/ti/ti_wdt.c optional ti_wdt arm/ti/ti_adc.c optional ti_adc arm/ti/ti_gpio.c optional gpio arm/ti/ti_gpio_if.m optional gpio arm/ti/ti_i2c.c optional ti_i2c arm/ti/ti_sdhci.c optional sdhci arm/ti/ti_spi.c optional ti_spi arm/ti/ti_sysc.c standard + +arm/ti/clk/clock_common.c standard +arm/ti/clk/ti_clk_clkctrl.c standard +arm/ti/clk/ti_clkctrl.c standard +arm/ti/clk/ti_clk_dpll.c standard +arm/ti/clk/ti_dpll_clock.c standard +arm/ti/clk/ti_mux_clock.c standard +arm/ti/clk/ti_divider_clock.c standard +arm/ti/clk/ti_gate_clock.c standard dev/uart/uart_dev_ti8250.c optional uart dev/uart/uart_dev_ns8250.c optional uart Index: head/sys/arm/ti/omap4/files.omap4 =================================================================== --- head/sys/arm/ti/omap4/files.omap4 (revision 363699) +++ head/sys/arm/ti/omap4/files.omap4 (revision 363700) @@ -1,22 +1,22 @@ #$FreeBSD$ arm/ti/ti_smc.S standard arm/ti/usb/omap_ehci.c optional usb ehci arm/ti/usb/omap_host.c optional usb arm/ti/usb/omap_tll.c optional usb arm/ti/ti_sdma.c optional ti_sdma arm/ti/omap4/omap4_gpio.c optional gpio arm/ti/omap4/omap4_l2cache.c optional pl310 -arm/ti/omap4/omap4_prcm_clks.c standard +#arm/ti/omap4/omap4_prcm_clks.c standard arm/ti/omap4/omap4_scm_padconf.c standard arm/ti/omap4/omap4_mp.c optional smp arm/ti/omap4/omap4_wugen.c standard arm/ti/omap4/pandaboard/pandaboard.c standard arm/ti/twl/twl.c optional twl arm/ti/twl/twl_vreg.c optional twl twl_vreg arm/ti/twl/twl_clks.c optional twl twl_clks Index: head/sys/arm/ti/ti_adc.c =================================================================== --- head/sys/arm/ti/ti_adc.c (revision 363699) +++ head/sys/arm/ti/ti_adc.c (revision 363700) @@ -1,969 +1,970 @@ /*- * Copyright 2014 Luiz Otavio O Souza * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include "opt_evdev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EVDEV_SUPPORT #include #include #endif -#include +#include #include #include #undef DEBUG_TSC #define DEFAULT_CHARGE_DELAY 0x400 #define STEPDLY_OPEN 0x98 #define ORDER_XP 0 #define ORDER_XN 1 #define ORDER_YP 2 #define ORDER_YN 3 /* Define our 8 steps, one for each input channel. */ static struct ti_adc_input ti_adc_inputs[TI_ADC_NPINS] = { { .stepconfig = ADC_STEPCFG(1), .stepdelay = ADC_STEPDLY(1) }, { .stepconfig = ADC_STEPCFG(2), .stepdelay = ADC_STEPDLY(2) }, { .stepconfig = ADC_STEPCFG(3), .stepdelay = ADC_STEPDLY(3) }, { .stepconfig = ADC_STEPCFG(4), .stepdelay = ADC_STEPDLY(4) }, { .stepconfig = ADC_STEPCFG(5), .stepdelay = ADC_STEPDLY(5) }, { .stepconfig = ADC_STEPCFG(6), .stepdelay = ADC_STEPDLY(6) }, { .stepconfig = ADC_STEPCFG(7), .stepdelay = ADC_STEPDLY(7) }, { .stepconfig = ADC_STEPCFG(8), .stepdelay = ADC_STEPDLY(8) }, }; static int ti_adc_samples[5] = { 0, 2, 4, 8, 16 }; static int ti_adc_detach(device_t dev); #ifdef EVDEV_SUPPORT static void ti_adc_ev_report(struct ti_adc_softc *sc) { evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, sc->sc_x); evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, sc->sc_y); evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, sc->sc_pen_down); evdev_sync(sc->sc_evdev); } #endif /* EVDEV */ static void ti_adc_enable(struct ti_adc_softc *sc) { uint32_t reg; TI_ADC_LOCK_ASSERT(sc); if (sc->sc_last_state == 1) return; /* Enable the FIFO0 threshold and the end of sequence interrupt. */ ADC_WRITE4(sc, ADC_IRQENABLE_SET, ADC_IRQ_FIFO0_THRES | ADC_IRQ_FIFO1_THRES | ADC_IRQ_END_OF_SEQ); reg = ADC_CTRL_STEP_WP | ADC_CTRL_STEP_ID; if (sc->sc_tsc_wires > 0) { reg |= ADC_CTRL_TSC_ENABLE; switch (sc->sc_tsc_wires) { case 4: reg |= ADC_CTRL_TSC_4WIRE; break; case 5: reg |= ADC_CTRL_TSC_5WIRE; break; case 8: reg |= ADC_CTRL_TSC_8WIRE; break; default: break; } } reg |= ADC_CTRL_ENABLE; /* Enable the ADC. Run thru enabled steps, start the conversions. */ ADC_WRITE4(sc, ADC_CTRL, reg); sc->sc_last_state = 1; } static void ti_adc_disable(struct ti_adc_softc *sc) { int count; uint32_t data; TI_ADC_LOCK_ASSERT(sc); if (sc->sc_last_state == 0) return; /* Disable all the enabled steps. */ ADC_WRITE4(sc, ADC_STEPENABLE, 0); /* Disable the ADC. */ ADC_WRITE4(sc, ADC_CTRL, ADC_READ4(sc, ADC_CTRL) & ~ADC_CTRL_ENABLE); /* Disable the FIFO0 threshold and the end of sequence interrupt. */ ADC_WRITE4(sc, ADC_IRQENABLE_CLR, ADC_IRQ_FIFO0_THRES | ADC_IRQ_FIFO1_THRES | ADC_IRQ_END_OF_SEQ); /* ACK any pending interrupt. */ ADC_WRITE4(sc, ADC_IRQSTATUS, ADC_READ4(sc, ADC_IRQSTATUS)); /* Drain the FIFO data. */ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK; while (count > 0) { data = ADC_READ4(sc, ADC_FIFO0DATA); count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK; } count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK; while (count > 0) { data = ADC_READ4(sc, ADC_FIFO1DATA); count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK; } sc->sc_last_state = 0; } static int ti_adc_setup(struct ti_adc_softc *sc) { int ain, i; uint32_t enabled; TI_ADC_LOCK_ASSERT(sc); /* Check for enabled inputs. */ enabled = sc->sc_tsc_enabled; for (i = 0; i < sc->sc_adc_nchannels; i++) { ain = sc->sc_adc_channels[i]; if (ti_adc_inputs[ain].enable) enabled |= (1U << (ain + 1)); } /* Set the ADC global status. */ if (enabled != 0) { ti_adc_enable(sc); /* Update the enabled steps. */ if (enabled != ADC_READ4(sc, ADC_STEPENABLE)) ADC_WRITE4(sc, ADC_STEPENABLE, enabled); } else ti_adc_disable(sc); return (0); } static void ti_adc_input_setup(struct ti_adc_softc *sc, int32_t ain) { struct ti_adc_input *input; uint32_t reg, val; TI_ADC_LOCK_ASSERT(sc); input = &ti_adc_inputs[ain]; reg = input->stepconfig; val = ADC_READ4(sc, reg); /* Set single ended operation. */ val &= ~ADC_STEP_DIFF_CNTRL; /* Set the negative voltage reference. */ val &= ~ADC_STEP_RFM_MSK; /* Set the positive voltage reference. */ val &= ~ADC_STEP_RFP_MSK; /* Set the samples average. */ val &= ~ADC_STEP_AVG_MSK; val |= input->samples << ADC_STEP_AVG_SHIFT; /* Select the desired input. */ val &= ~ADC_STEP_INP_MSK; val |= ain << ADC_STEP_INP_SHIFT; /* Set the ADC to one-shot mode. */ val &= ~ADC_STEP_MODE_MSK; ADC_WRITE4(sc, reg, val); } static void ti_adc_reset(struct ti_adc_softc *sc) { int ain, i; TI_ADC_LOCK_ASSERT(sc); /* Disable all the inputs. */ for (i = 0; i < sc->sc_adc_nchannels; i++) { ain = sc->sc_adc_channels[i]; ti_adc_inputs[ain].enable = 0; } } static int ti_adc_clockdiv_proc(SYSCTL_HANDLER_ARGS) { int error, reg; struct ti_adc_softc *sc; sc = (struct ti_adc_softc *)arg1; TI_ADC_LOCK(sc); reg = (int)ADC_READ4(sc, ADC_CLKDIV) + 1; TI_ADC_UNLOCK(sc); error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); /* * The actual written value is the prescaler setting - 1. * Enforce a minimum value of 10 (i.e. 9) which limits the maximum * ADC clock to ~2.4Mhz (CLK_M_OSC / 10). */ reg--; if (reg < 9) reg = 9; if (reg > USHRT_MAX) reg = USHRT_MAX; TI_ADC_LOCK(sc); /* Disable the ADC. */ ti_adc_disable(sc); /* Update the ADC prescaler setting. */ ADC_WRITE4(sc, ADC_CLKDIV, reg); /* Enable the ADC again. */ ti_adc_setup(sc); TI_ADC_UNLOCK(sc); return (0); } static int ti_adc_enable_proc(SYSCTL_HANDLER_ARGS) { int error; int32_t enable; struct ti_adc_softc *sc; struct ti_adc_input *input; input = (struct ti_adc_input *)arg1; sc = input->sc; enable = input->enable; error = sysctl_handle_int(oidp, &enable, sizeof(enable), req); if (error != 0 || req->newptr == NULL) return (error); if (enable) enable = 1; TI_ADC_LOCK(sc); /* Setup the ADC as needed. */ if (input->enable != enable) { input->enable = enable; ti_adc_setup(sc); if (input->enable == 0) input->value = 0; } TI_ADC_UNLOCK(sc); return (0); } static int ti_adc_open_delay_proc(SYSCTL_HANDLER_ARGS) { int error, reg; struct ti_adc_softc *sc; struct ti_adc_input *input; input = (struct ti_adc_input *)arg1; sc = input->sc; TI_ADC_LOCK(sc); reg = (int)ADC_READ4(sc, input->stepdelay) & ADC_STEP_OPEN_DELAY; TI_ADC_UNLOCK(sc); error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); if (reg < 0) reg = 0; TI_ADC_LOCK(sc); ADC_WRITE4(sc, input->stepdelay, reg & ADC_STEP_OPEN_DELAY); TI_ADC_UNLOCK(sc); return (0); } static int ti_adc_samples_avg_proc(SYSCTL_HANDLER_ARGS) { int error, samples, i; struct ti_adc_softc *sc; struct ti_adc_input *input; input = (struct ti_adc_input *)arg1; sc = input->sc; if (input->samples > nitems(ti_adc_samples)) input->samples = nitems(ti_adc_samples); samples = ti_adc_samples[input->samples]; error = sysctl_handle_int(oidp, &samples, 0, req); if (error != 0 || req->newptr == NULL) return (error); TI_ADC_LOCK(sc); if (samples != ti_adc_samples[input->samples]) { input->samples = 0; for (i = 0; i < nitems(ti_adc_samples); i++) if (samples >= ti_adc_samples[i]) input->samples = i; ti_adc_input_setup(sc, input->input); } TI_ADC_UNLOCK(sc); return (error); } static void ti_adc_read_data(struct ti_adc_softc *sc) { int count, ain; struct ti_adc_input *input; uint32_t data; TI_ADC_LOCK_ASSERT(sc); /* Read the available data. */ count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK; while (count > 0) { data = ADC_READ4(sc, ADC_FIFO0DATA); ain = (data & ADC_FIFO_STEP_ID_MSK) >> ADC_FIFO_STEP_ID_SHIFT; input = &ti_adc_inputs[ain]; if (input->enable == 0) input->value = 0; else input->value = (int32_t)(data & ADC_FIFO_DATA_MSK); count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK; } } static int cmp_values(const void *a, const void *b) { const uint32_t *v1, *v2; v1 = a; v2 = b; if (*v1 < *v2) return -1; if (*v1 > *v2) return 1; return (0); } static void ti_adc_tsc_read_data(struct ti_adc_softc *sc) { int count; uint32_t data[16]; uint32_t x, y; int i, start, end; TI_ADC_LOCK_ASSERT(sc); /* Read the available data. */ count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK; if (count == 0) return; i = 0; while (count > 0) { data[i++] = ADC_READ4(sc, ADC_FIFO1DATA) & ADC_FIFO_DATA_MSK; count = ADC_READ4(sc, ADC_FIFO1COUNT) & ADC_FIFO_COUNT_MSK; } if (sc->sc_coord_readouts > 3) { start = 1; end = sc->sc_coord_readouts - 1; qsort(data, sc->sc_coord_readouts, sizeof(data[0]), &cmp_values); qsort(&data[sc->sc_coord_readouts + 2], sc->sc_coord_readouts, sizeof(data[0]), &cmp_values); } else { start = 0; end = sc->sc_coord_readouts; } x = y = 0; for (i = start; i < end; i++) y += data[i]; y /= (end - start); for (i = sc->sc_coord_readouts + 2 + start; i < sc->sc_coord_readouts + 2 + end; i++) x += data[i]; x /= (end - start); #ifdef DEBUG_TSC device_printf(sc->sc_dev, "touchscreen x: %d, y: %d\n", x, y); #endif #ifdef EVDEV_SUPPORT if ((sc->sc_x != x) || (sc->sc_y != y)) { sc->sc_x = x; sc->sc_y = y; ti_adc_ev_report(sc); } #endif } static void ti_adc_intr_locked(struct ti_adc_softc *sc, uint32_t status) { /* Read the available data. */ if (status & ADC_IRQ_FIFO0_THRES) ti_adc_read_data(sc); } static void ti_adc_tsc_intr_locked(struct ti_adc_softc *sc, uint32_t status) { /* Read the available data. */ if (status & ADC_IRQ_FIFO1_THRES) ti_adc_tsc_read_data(sc); } static void ti_adc_intr(void *arg) { struct ti_adc_softc *sc; uint32_t status, rawstatus; sc = (struct ti_adc_softc *)arg; TI_ADC_LOCK(sc); rawstatus = ADC_READ4(sc, ADC_IRQSTATUS_RAW); status = ADC_READ4(sc, ADC_IRQSTATUS); if (rawstatus & ADC_IRQ_HW_PEN_ASYNC) { sc->sc_pen_down = 1; status |= ADC_IRQ_HW_PEN_ASYNC; ADC_WRITE4(sc, ADC_IRQENABLE_CLR, ADC_IRQ_HW_PEN_ASYNC); #ifdef EVDEV_SUPPORT ti_adc_ev_report(sc); #endif } if (rawstatus & ADC_IRQ_PEN_UP) { sc->sc_pen_down = 0; status |= ADC_IRQ_PEN_UP; #ifdef EVDEV_SUPPORT ti_adc_ev_report(sc); #endif } if (status & ADC_IRQ_FIFO0_THRES) ti_adc_intr_locked(sc, status); if (status & ADC_IRQ_FIFO1_THRES) ti_adc_tsc_intr_locked(sc, status); if (status) { /* ACK the interrupt. */ ADC_WRITE4(sc, ADC_IRQSTATUS, status); } /* Start the next conversion ? */ if (status & ADC_IRQ_END_OF_SEQ) ti_adc_setup(sc); TI_ADC_UNLOCK(sc); } static void ti_adc_sysctl_init(struct ti_adc_softc *sc) { char pinbuf[3]; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node, *inp_node, *inpN_node; struct sysctl_oid_list *tree, *inp_tree, *inpN_tree; int ain, i; /* * Add per-pin sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clockdiv", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0, ti_adc_clockdiv_proc, "IU", "ADC clock prescaler"); inp_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "ain", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "ADC inputs"); inp_tree = SYSCTL_CHILDREN(inp_node); for (i = 0; i < sc->sc_adc_nchannels; i++) { ain = sc->sc_adc_channels[i]; snprintf(pinbuf, sizeof(pinbuf), "%d", ain); inpN_node = SYSCTL_ADD_NODE(ctx, inp_tree, OID_AUTO, pinbuf, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "ADC input"); inpN_tree = SYSCTL_CHILDREN(inpN_node); SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "enable", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, &ti_adc_inputs[ain], 0, ti_adc_enable_proc, "IU", "Enable ADC input"); SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "open_delay", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, &ti_adc_inputs[ain], 0, ti_adc_open_delay_proc, "IU", "ADC open delay"); SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "samples_avg", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, &ti_adc_inputs[ain], 0, ti_adc_samples_avg_proc, "IU", "ADC samples average"); SYSCTL_ADD_INT(ctx, inpN_tree, OID_AUTO, "input", CTLFLAG_RD, &ti_adc_inputs[ain].value, 0, "Converted raw value for the ADC input"); } } static void ti_adc_inputs_init(struct ti_adc_softc *sc) { int ain, i; struct ti_adc_input *input; TI_ADC_LOCK(sc); for (i = 0; i < sc->sc_adc_nchannels; i++) { ain = sc->sc_adc_channels[i]; input = &ti_adc_inputs[ain]; input->sc = sc; input->input = ain; input->value = 0; input->enable = 0; input->samples = 0; ti_adc_input_setup(sc, ain); } TI_ADC_UNLOCK(sc); } static void ti_adc_tsc_init(struct ti_adc_softc *sc) { int i, start_step, end_step; uint32_t stepconfig, val; TI_ADC_LOCK(sc); /* X coordinates */ stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) | ADC_STEP_MODE_HW_ONESHOT | sc->sc_xp_bit; if (sc->sc_tsc_wires == 4) stepconfig |= ADC_STEP_INP(sc->sc_yp_inp) | sc->sc_xn_bit; else if (sc->sc_tsc_wires == 5) stepconfig |= ADC_STEP_INP(4) | sc->sc_xn_bit | sc->sc_yn_bit | sc->sc_yp_bit; else if (sc->sc_tsc_wires == 8) stepconfig |= ADC_STEP_INP(sc->sc_yp_inp) | sc->sc_xn_bit; start_step = ADC_STEPS - sc->sc_coord_readouts + 1; end_step = start_step + sc->sc_coord_readouts - 1; for (i = start_step; i <= end_step; i++) { ADC_WRITE4(sc, ADC_STEPCFG(i), stepconfig); ADC_WRITE4(sc, ADC_STEPDLY(i), STEPDLY_OPEN); } /* Y coordinates */ stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) | ADC_STEP_MODE_HW_ONESHOT | sc->sc_yn_bit | ADC_STEP_INM(8); if (sc->sc_tsc_wires == 4) stepconfig |= ADC_STEP_INP(sc->sc_xp_inp) | sc->sc_yp_bit; else if (sc->sc_tsc_wires == 5) stepconfig |= ADC_STEP_INP(4) | sc->sc_xp_bit | sc->sc_xn_bit | sc->sc_yp_bit; else if (sc->sc_tsc_wires == 8) stepconfig |= ADC_STEP_INP(sc->sc_xp_inp) | sc->sc_yp_bit; start_step = ADC_STEPS - (sc->sc_coord_readouts*2 + 2) + 1; end_step = start_step + sc->sc_coord_readouts - 1; for (i = start_step; i <= end_step; i++) { ADC_WRITE4(sc, ADC_STEPCFG(i), stepconfig); ADC_WRITE4(sc, ADC_STEPDLY(i), STEPDLY_OPEN); } /* Charge config */ val = ADC_READ4(sc, ADC_IDLECONFIG); ADC_WRITE4(sc, ADC_TC_CHARGE_STEPCONFIG, val); ADC_WRITE4(sc, ADC_TC_CHARGE_DELAY, sc->sc_charge_delay); /* 2 steps for Z */ start_step = ADC_STEPS - (sc->sc_coord_readouts + 2) + 1; stepconfig = ADC_STEP_FIFO1 | (4 << ADC_STEP_AVG_SHIFT) | ADC_STEP_MODE_HW_ONESHOT | sc->sc_yp_bit | sc->sc_xn_bit | ADC_STEP_INP(sc->sc_xp_inp) | ADC_STEP_INM(8); ADC_WRITE4(sc, ADC_STEPCFG(start_step), stepconfig); ADC_WRITE4(sc, ADC_STEPDLY(start_step), STEPDLY_OPEN); start_step++; stepconfig |= ADC_STEP_INP(sc->sc_yn_inp); ADC_WRITE4(sc, ADC_STEPCFG(start_step), stepconfig); ADC_WRITE4(sc, ADC_STEPDLY(start_step), STEPDLY_OPEN); ADC_WRITE4(sc, ADC_FIFO1THRESHOLD, (sc->sc_coord_readouts*2 + 2) - 1); sc->sc_tsc_enabled = 1; start_step = ADC_STEPS - (sc->sc_coord_readouts*2 + 2) + 1; end_step = ADC_STEPS; for (i = start_step; i <= end_step; i++) { sc->sc_tsc_enabled |= (1 << i); } TI_ADC_UNLOCK(sc); } static void ti_adc_idlestep_init(struct ti_adc_softc *sc) { uint32_t val; val = ADC_STEP_YNN_SW | ADC_STEP_INM(8) | ADC_STEP_INP(8) | ADC_STEP_YPN_SW; ADC_WRITE4(sc, ADC_IDLECONFIG, val); } static int ti_adc_config_wires(struct ti_adc_softc *sc, int *wire_configs, int nwire_configs) { int i; int wire, ai; for (i = 0; i < nwire_configs; i++) { wire = wire_configs[i] & 0xf; ai = (wire_configs[i] >> 4) & 0xf; switch (wire) { case ORDER_XP: sc->sc_xp_bit = ADC_STEP_XPP_SW; sc->sc_xp_inp = ai; break; case ORDER_XN: sc->sc_xn_bit = ADC_STEP_XNN_SW; sc->sc_xn_inp = ai; break; case ORDER_YP: sc->sc_yp_bit = ADC_STEP_YPP_SW; sc->sc_yp_inp = ai; break; case ORDER_YN: sc->sc_yn_bit = ADC_STEP_YNN_SW; sc->sc_yn_inp = ai; break; default: device_printf(sc->sc_dev, "Invalid wire config\n"); return (-1); } } return (0); } static int ti_adc_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "ti,am3359-tscadc")) return (ENXIO); device_set_desc(dev, "TI ADC controller"); return (BUS_PROBE_DEFAULT); } static int ti_adc_attach(device_t dev) { int err, rid, i; struct ti_adc_softc *sc; uint32_t rev, reg; phandle_t node, child; pcell_t cell; int *channels; int nwire_configs; int *wire_configs; sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); sc->sc_tsc_wires = 0; sc->sc_coord_readouts = 1; sc->sc_x_plate_resistance = 0; sc->sc_charge_delay = DEFAULT_CHARGE_DELAY; /* Read "tsc" node properties */ child = ofw_bus_find_child(node, "tsc"); if (child != 0 && OF_hasprop(child, "ti,wires")) { if ((OF_getencprop(child, "ti,wires", &cell, sizeof(cell))) > 0) sc->sc_tsc_wires = cell; if ((OF_getencprop(child, "ti,coordinate-readouts", &cell, sizeof(cell))) > 0) sc->sc_coord_readouts = cell; if ((OF_getencprop(child, "ti,x-plate-resistance", &cell, sizeof(cell))) > 0) sc->sc_x_plate_resistance = cell; if ((OF_getencprop(child, "ti,charge-delay", &cell, sizeof(cell))) > 0) sc->sc_charge_delay = cell; nwire_configs = OF_getencprop_alloc_multi(child, "ti,wire-config", sizeof(*wire_configs), (void **)&wire_configs); if (nwire_configs != sc->sc_tsc_wires) { device_printf(sc->sc_dev, "invalid number of ti,wire-config: %d (should be %d)\n", nwire_configs, sc->sc_tsc_wires); OF_prop_free(wire_configs); return (EINVAL); } err = ti_adc_config_wires(sc, wire_configs, nwire_configs); OF_prop_free(wire_configs); if (err) return (EINVAL); } /* Read "adc" node properties */ child = ofw_bus_find_child(node, "adc"); if (child != 0) { sc->sc_adc_nchannels = OF_getencprop_alloc_multi(child, "ti,adc-channels", sizeof(*channels), (void **)&channels); if (sc->sc_adc_nchannels > 0) { for (i = 0; i < sc->sc_adc_nchannels; i++) sc->sc_adc_channels[i] = channels[i]; OF_prop_free(channels); } } /* Sanity check FDT data */ if (sc->sc_tsc_wires + sc->sc_adc_nchannels > TI_ADC_NPINS) { device_printf(dev, "total number of chanels (%d) is larger than %d\n", sc->sc_tsc_wires + sc->sc_adc_nchannels, TI_ADC_NPINS); return (ENXIO); } rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } /* Activate the ADC_TSC module. */ - err = ti_prcm_clk_enable(TSC_ADC_CLK); + err = ti_sysc_clock_enable(device_get_parent(dev)); if (err) return (err); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_adc_intr, sc, &sc->sc_intrhand) != 0) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "Unable to setup the irq handler.\n"); return (ENXIO); } /* Check the ADC revision. */ - rev = ADC_READ4(sc, ADC_REVISION); + rev = ADC_READ4(sc, ti_sysc_get_rev_address_offset_host(device_get_parent(dev))); device_printf(dev, "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n", (rev & ADC_REV_SCHEME_MSK) >> ADC_REV_SCHEME_SHIFT, (rev & ADC_REV_FUNC_MSK) >> ADC_REV_FUNC_SHIFT, (rev & ADC_REV_RTL_MSK) >> ADC_REV_RTL_SHIFT, (rev & ADC_REV_MAJOR_MSK) >> ADC_REV_MAJOR_SHIFT, rev & ADC_REV_MINOR_MSK, (rev & ADC_REV_CUSTOM_MSK) >> ADC_REV_CUSTOM_SHIFT); reg = ADC_READ4(sc, ADC_CTRL); ADC_WRITE4(sc, ADC_CTRL, reg | ADC_CTRL_STEP_WP | ADC_CTRL_STEP_ID); /* * Set the ADC prescaler to 2400 if touchscreen is not enabled * and to 24 if it is. This sets the ADC clock to ~10Khz and * ~1Mhz respectively (CLK_M_OSC / prescaler). */ if (sc->sc_tsc_wires) ADC_WRITE4(sc, ADC_CLKDIV, 24 - 1); else ADC_WRITE4(sc, ADC_CLKDIV, 2400 - 1); TI_ADC_LOCK_INIT(sc); ti_adc_idlestep_init(sc); ti_adc_inputs_init(sc); ti_adc_sysctl_init(sc); ti_adc_tsc_init(sc); TI_ADC_LOCK(sc); ti_adc_setup(sc); TI_ADC_UNLOCK(sc); #ifdef EVDEV_SUPPORT if (sc->sc_tsc_wires > 0) { sc->sc_evdev = evdev_alloc(); evdev_set_name(sc->sc_evdev, device_get_desc(dev)); evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev)); evdev_set_id(sc->sc_evdev, BUS_VIRTUAL, 0, 0, 0); evdev_support_prop(sc->sc_evdev, INPUT_PROP_DIRECT); evdev_support_event(sc->sc_evdev, EV_SYN); evdev_support_event(sc->sc_evdev, EV_ABS); evdev_support_event(sc->sc_evdev, EV_KEY); evdev_support_abs(sc->sc_evdev, ABS_X, 0, 0, ADC_MAX_VALUE, 0, 0, 0); evdev_support_abs(sc->sc_evdev, ABS_Y, 0, 0, ADC_MAX_VALUE, 0, 0, 0); evdev_support_key(sc->sc_evdev, BTN_TOUCH); err = evdev_register(sc->sc_evdev); if (err) { device_printf(dev, "failed to register evdev: error=%d\n", err); ti_adc_detach(dev); return (err); } sc->sc_pen_down = 0; sc->sc_x = -1; sc->sc_y = -1; } #endif /* EVDEV */ return (0); } static int ti_adc_detach(device_t dev) { struct ti_adc_softc *sc; sc = device_get_softc(dev); /* Turn off the ADC. */ TI_ADC_LOCK(sc); ti_adc_reset(sc); ti_adc_setup(sc); #ifdef EVDEV_SUPPORT evdev_free(sc->sc_evdev); #endif TI_ADC_UNLOCK(sc); TI_ADC_LOCK_DESTROY(sc); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (bus_generic_detach(dev)); } static device_method_t ti_adc_methods[] = { DEVMETHOD(device_probe, ti_adc_probe), DEVMETHOD(device_attach, ti_adc_attach), DEVMETHOD(device_detach, ti_adc_detach), DEVMETHOD_END }; static driver_t ti_adc_driver = { "ti_adc", ti_adc_methods, sizeof(struct ti_adc_softc), }; static devclass_t ti_adc_devclass; DRIVER_MODULE(ti_adc, simplebus, ti_adc_driver, ti_adc_devclass, 0, 0); MODULE_VERSION(ti_adc, 1); MODULE_DEPEND(ti_adc, simplebus, 1, 1, 1); +MODULE_DEPEND(ti_adc, ti_sysc, 1, 1, 1); #ifdef EVDEV_SUPPORT MODULE_DEPEND(ti_adc, evdev, 1, 1, 1); #endif Index: head/sys/arm/ti/ti_edma3.c =================================================================== --- head/sys/arm/ti/ti_edma3.c (revision 363699) +++ head/sys/arm/ti/ti_edma3.c (revision 363700) @@ -1,428 +1,425 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (c) 2012 Damjan Marion * 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. * 3. Neither the name of authors nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #define TI_EDMA3_NUM_TCS 3 #define TI_EDMA3_NUM_IRQS 3 #define TI_EDMA3_NUM_DMA_CHS 64 #define TI_EDMA3_NUM_QDMA_CHS 8 #define TI_EDMA3CC_PID 0x000 #define TI_EDMA3CC_DCHMAP(p) (0x100 + ((p)*4)) #define TI_EDMA3CC_DMAQNUM(n) (0x240 + ((n)*4)) #define TI_EDMA3CC_QDMAQNUM 0x260 #define TI_EDMA3CC_EMCR 0x308 #define TI_EDMA3CC_EMCRH 0x30C #define TI_EDMA3CC_QEMCR 0x314 #define TI_EDMA3CC_CCERR 0x318 #define TI_EDMA3CC_CCERRCLR 0x31C #define TI_EDMA3CC_DRAE(p) (0x340 + ((p)*8)) #define TI_EDMA3CC_DRAEH(p) (0x344 + ((p)*8)) #define TI_EDMA3CC_QRAE(p) (0x380 + ((p)*4)) #define TI_EDMA3CC_S_ESR(p) (0x2010 + ((p)*0x200)) #define TI_EDMA3CC_S_ESRH(p) (0x2014 + ((p)*0x200)) #define TI_EDMA3CC_S_SECR(p) (0x2040 + ((p)*0x200)) #define TI_EDMA3CC_S_SECRH(p) (0x2044 + ((p)*0x200)) #define TI_EDMA3CC_S_EESR(p) (0x2030 + ((p)*0x200)) #define TI_EDMA3CC_S_EESRH(p) (0x2034 + ((p)*0x200)) #define TI_EDMA3CC_S_IESR(p) (0x2060 + ((p)*0x200)) #define TI_EDMA3CC_S_IESRH(p) (0x2064 + ((p)*0x200)) #define TI_EDMA3CC_S_IPR(p) (0x2068 + ((p)*0x200)) #define TI_EDMA3CC_S_IPRH(p) (0x206C + ((p)*0x200)) #define TI_EDMA3CC_S_QEESR(p) (0x208C + ((p)*0x200)) #define TI_EDMA3CC_PARAM_OFFSET 0x4000 #define TI_EDMA3CC_OPT(p) (TI_EDMA3CC_PARAM_OFFSET + 0x0 + ((p)*0x20)) #define TI_EDMA3CC_DMAQNUM_SET(c,q) ((0x7 & (q)) << (((c) % 8) * 4)) #define TI_EDMA3CC_DMAQNUM_CLR(c) (~(0x7 << (((c) % 8) * 4))) #define TI_EDMA3CC_QDMAQNUM_SET(c,q) ((0x7 & (q)) << ((c) * 4)) #define TI_EDMA3CC_QDMAQNUM_CLR(c) (~(0x7 << ((c) * 4))) #define TI_EDMA3CC_OPT_TCC_CLR (~(0x3F000)) #define TI_EDMA3CC_OPT_TCC_SET(p) (((0x3F000 >> 12) & (p)) << 12) struct ti_edma3_softc { device_t sc_dev; /* * We use one-element array in case if we need to add * mem resources for transfer control windows */ struct resource * mem_res[1]; struct resource * irq_res[TI_EDMA3_NUM_IRQS]; void *ih_cookie[TI_EDMA3_NUM_IRQS]; }; static struct ti_edma3_softc *ti_edma3_sc = NULL; static struct resource_spec ti_edma3_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec ti_edma3_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { -1, 0, 0 } }; /* Read/Write macros */ #define ti_edma3_cc_rd_4(reg) bus_read_4(ti_edma3_sc->mem_res[0], reg) #define ti_edma3_cc_wr_4(reg, val) bus_write_4(ti_edma3_sc->mem_res[0], reg, val) static void ti_edma3_intr_comp(void *arg); static void ti_edma3_intr_mperr(void *arg); static void ti_edma3_intr_err(void *arg); static struct { driver_intr_t *handler; char * description; } ti_edma3_intrs[TI_EDMA3_NUM_IRQS] = { { ti_edma3_intr_comp, "EDMA Completion Interrupt" }, { ti_edma3_intr_mperr, "EDMA Memory Protection Error Interrupt" }, { ti_edma3_intr_err, "EDMA Error Interrupt" }, }; static int ti_edma3_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,edma3")) return (ENXIO); device_set_desc(dev, "TI EDMA Controller"); return (0); } static int ti_edma3_attach(device_t dev) { struct ti_edma3_softc *sc = device_get_softc(dev); uint32_t reg; int err; int i; if (ti_edma3_sc) return (ENXIO); ti_edma3_sc = sc; sc->sc_dev = dev; /* Request the memory resources */ err = bus_alloc_resources(dev, ti_edma3_mem_spec, sc->mem_res); if (err) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources */ err = bus_alloc_resources(dev, ti_edma3_irq_spec, sc->irq_res); if (err) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } + /* FIXME: Require DTS from Linux kernel 5.7 */ + /* FIXME: OK to enable clkctrl here? */ /* Enable Channel Controller */ - ti_prcm_clk_enable(EDMA_TPCC_CLK); + ti_sysc_clock_enable(device_get_parent(dev)); reg = ti_edma3_cc_rd_4(TI_EDMA3CC_PID); device_printf(dev, "EDMA revision %08x\n", reg); /* Attach interrupt handlers */ for (i = 0; i < TI_EDMA3_NUM_IRQS; ++i) { err = bus_setup_intr(dev, sc->irq_res[i], INTR_TYPE_MISC | INTR_MPSAFE, NULL, *ti_edma3_intrs[i].handler, sc, &sc->ih_cookie[i]); if (err) { device_printf(dev, "could not setup %s\n", ti_edma3_intrs[i].description); return (err); } } return (0); } static device_method_t ti_edma3_methods[] = { DEVMETHOD(device_probe, ti_edma3_probe), DEVMETHOD(device_attach, ti_edma3_attach), {0, 0}, }; static driver_t ti_edma3_driver = { "ti_edma3", ti_edma3_methods, sizeof(struct ti_edma3_softc), }; static devclass_t ti_edma3_devclass; DRIVER_MODULE(ti_edma3, simplebus, ti_edma3_driver, ti_edma3_devclass, 0, 0); -MODULE_DEPEND(ti_edma3, ti_prcm, 1, 1, 1); +MODULE_DEPEND(ti_edma3, ti_sysc, 1, 1, 1); static void ti_edma3_intr_comp(void *arg) { printf("%s: unimplemented\n", __func__); } static void ti_edma3_intr_mperr(void *arg) { printf("%s: unimplemented\n", __func__); } static void ti_edma3_intr_err(void *arg) { printf("%s: unimplemented\n", __func__); } void ti_edma3_init(unsigned int eqn) { uint32_t reg; int i; - - /* on AM335x Event queue 0 is always mapped to Transfer Controller 0, - * event queue 1 to TC2, etc. So we are asking PRCM to power on specific - * TC based on what event queue we need to initialize */ - ti_prcm_clk_enable(EDMA_TPTC0_CLK + eqn); /* Clear Event Missed Regs */ ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, 0xFFFFFFFF); ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 0xFFFFFFFF); ti_edma3_cc_wr_4(TI_EDMA3CC_QEMCR, 0xFFFFFFFF); /* Clear Error Reg */ ti_edma3_cc_wr_4(TI_EDMA3CC_CCERRCLR, 0xFFFFFFFF); /* Enable DMA channels 0-63 */ ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), 0xFFFFFFFF); ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), 0xFFFFFFFF); for (i = 0; i < 64; i++) { ti_edma3_cc_wr_4(TI_EDMA3CC_DCHMAP(i), i<<5); } /* Initialize the DMA Queue Number Registers */ for (i = 0; i < TI_EDMA3_NUM_DMA_CHS; i++) { reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(i>>3)); reg &= TI_EDMA3CC_DMAQNUM_CLR(i); reg |= TI_EDMA3CC_DMAQNUM_SET(i, eqn); ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(i>>3), reg); } /* Enable the QDMA Region access for all channels */ ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), (1 << TI_EDMA3_NUM_QDMA_CHS) - 1); /*Initialize QDMA Queue Number Registers */ for (i = 0; i < TI_EDMA3_NUM_QDMA_CHS; i++) { reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM); reg &= TI_EDMA3CC_QDMAQNUM_CLR(i); reg |= TI_EDMA3CC_QDMAQNUM_SET(i, eqn); ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg); } } #ifdef notyet int ti_edma3_enable_event_intr(unsigned int ch) { uint32_t reg; if (ch >= TI_EDMA3_NUM_DMA_CHS) return (EINVAL); if (ch < 32) { ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESR(0), 1 << ch); } else { ti_edma3_cc_wr_4(TI_EDMA3CC_S_IESRH(0), 1 << (ch - 32)); } return 0; } #endif int ti_edma3_request_dma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn) { uint32_t reg; if (ch >= TI_EDMA3_NUM_DMA_CHS) return (EINVAL); /* Enable the DMA channel in the DRAE/DRAEH registers */ if (ch < 32) { reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAE(0)); reg |= (0x01 << ch); ti_edma3_cc_wr_4(TI_EDMA3CC_DRAE(0), reg); } else { reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DRAEH(0)); reg |= (0x01 << (ch - 32)); ti_edma3_cc_wr_4(TI_EDMA3CC_DRAEH(0), reg); } /* Associate DMA Channel to Event Queue */ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_DMAQNUM(ch >> 3)); reg &= TI_EDMA3CC_DMAQNUM_CLR(ch); reg |= TI_EDMA3CC_DMAQNUM_SET((ch), eqn); ti_edma3_cc_wr_4(TI_EDMA3CC_DMAQNUM(ch >> 3), reg); /* Set TCC in corresponding PaRAM Entry */ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch)); reg &= TI_EDMA3CC_OPT_TCC_CLR; reg |= TI_EDMA3CC_OPT_TCC_SET(ch); ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg); return 0; } int ti_edma3_request_qdma_ch(unsigned int ch, unsigned int tccn, unsigned int eqn) { uint32_t reg; if (ch >= TI_EDMA3_NUM_DMA_CHS) return (EINVAL); /* Enable the QDMA channel in the QRAE registers */ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QRAE(0)); reg |= (0x01 << ch); ti_edma3_cc_wr_4(TI_EDMA3CC_QRAE(0), reg); /* Associate QDMA Channel to Event Queue */ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_QDMAQNUM); reg |= TI_EDMA3CC_QDMAQNUM_SET(ch, eqn); ti_edma3_cc_wr_4(TI_EDMA3CC_QDMAQNUM, reg); /* Set TCC in corresponding PaRAM Entry */ reg = ti_edma3_cc_rd_4(TI_EDMA3CC_OPT(ch)); reg &= TI_EDMA3CC_OPT_TCC_CLR; reg |= TI_EDMA3CC_OPT_TCC_SET(ch); ti_edma3_cc_wr_4(TI_EDMA3CC_OPT(ch), reg); return 0; } int ti_edma3_enable_transfer_manual(unsigned int ch) { if (ch >= TI_EDMA3_NUM_DMA_CHS) return (EINVAL); /* set corresponding bit in ESR/ESRH to set a event */ if (ch < 32) { ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESR(0), 1 << ch); } else { ti_edma3_cc_wr_4(TI_EDMA3CC_S_ESRH(0), 1 << (ch - 32)); } return 0; } int ti_edma3_enable_transfer_qdma(unsigned int ch) { if (ch >= TI_EDMA3_NUM_QDMA_CHS) return (EINVAL); /* set corresponding bit in QEESR to enable QDMA event */ ti_edma3_cc_wr_4(TI_EDMA3CC_S_QEESR(0), (1 << ch)); return 0; } int ti_edma3_enable_transfer_event(unsigned int ch) { if (ch >= TI_EDMA3_NUM_DMA_CHS) return (EINVAL); /* Clear SECR(H) & EMCR(H) to clean any previous NULL request * and set corresponding bit in EESR to enable DMA event */ if(ch < 32) { ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECR(0), (1 << ch)); ti_edma3_cc_wr_4(TI_EDMA3CC_EMCR, (1 << ch)); ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESR(0), (1 << ch)); } else { ti_edma3_cc_wr_4(TI_EDMA3CC_S_SECRH(0), 1 << (ch - 32)); ti_edma3_cc_wr_4(TI_EDMA3CC_EMCRH, 1 << (ch - 32)); ti_edma3_cc_wr_4(TI_EDMA3CC_S_EESRH(0), 1 << (ch - 32)); } return 0; } void ti_edma3_param_write(unsigned int ch, struct ti_edma3cc_param_set *prs) { bus_write_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch), (uint32_t *) prs, 8); } void ti_edma3_param_read(unsigned int ch, struct ti_edma3cc_param_set *prs) { bus_read_region_4(ti_edma3_sc->mem_res[0], TI_EDMA3CC_OPT(ch), (uint32_t *) prs, 8); } Index: head/sys/arm/ti/ti_gpio.c =================================================================== --- head/sys/arm/ti/ti_gpio.c (revision 363699) +++ head/sys/arm/ti/ti_gpio.c (revision 363700) @@ -1,1087 +1,1141 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Ben Gray . * Copyright (c) 2014 Luiz Otavio O Souza . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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. */ /** * Beware that the OMAP4 datasheet(s) lists GPIO banks 1-6, whereas the code * here uses 0-5. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include #include #include #include #include #include "gpio_if.h" #include "ti_gpio_if.h" #include "pic_if.h" #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X) #error "Unknown SoC" #endif /* Register definitions */ #define TI_GPIO_REVISION 0x0000 #define TI_GPIO_SYSCONFIG 0x0010 #define TI_GPIO_IRQSTATUS_RAW_0 0x0024 #define TI_GPIO_IRQSTATUS_RAW_1 0x0028 #define TI_GPIO_IRQSTATUS_0 0x002C /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_1 0x0030 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_SET_0 0x0034 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_SET_1 0x0038 /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_CLR_0 0x003C /* writing a 0 has no effect */ #define TI_GPIO_IRQSTATUS_CLR_1 0x0040 /* writing a 0 has no effect */ #define TI_GPIO_IRQWAKEN_0 0x0044 #define TI_GPIO_IRQWAKEN_1 0x0048 #define TI_GPIO_SYSSTATUS 0x0114 #define TI_GPIO_IRQSTATUS1 0x0118 #define TI_GPIO_IRQENABLE1 0x011C #define TI_GPIO_WAKEUPENABLE 0x0120 #define TI_GPIO_IRQSTATUS2 0x0128 #define TI_GPIO_IRQENABLE2 0x012C #define TI_GPIO_CTRL 0x0130 #define TI_GPIO_OE 0x0134 #define TI_GPIO_DATAIN 0x0138 #define TI_GPIO_DATAOUT 0x013C #define TI_GPIO_LEVELDETECT0 0x0140 /* RW register */ #define TI_GPIO_LEVELDETECT1 0x0144 /* RW register */ #define TI_GPIO_RISINGDETECT 0x0148 /* RW register */ #define TI_GPIO_FALLINGDETECT 0x014C /* RW register */ #define TI_GPIO_DEBOUNCENABLE 0x0150 #define TI_GPIO_DEBOUNCINGTIME 0x0154 #define TI_GPIO_CLEARWKUPENA 0x0180 #define TI_GPIO_SETWKUENA 0x0184 #define TI_GPIO_CLEARDATAOUT 0x0190 #define TI_GPIO_SETDATAOUT 0x0194 /* Other SoC Specific definitions */ #define OMAP4_FIRST_GPIO_BANK 1 #define OMAP4_INTR_PER_BANK 1 #define OMAP4_GPIO_REV 0x50600801 #define AM335X_FIRST_GPIO_BANK 0 #define AM335X_INTR_PER_BANK 2 #define AM335X_GPIO_REV 0x50600801 #define PINS_PER_BANK 32 #define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK)) +#define OMAP4_GPIO1_REV 0x00000 +#define OMAP4_GPIO2_REV 0x55000 +#define OMAP4_GPIO3_REV 0x57000 +#define OMAP4_GPIO4_REV 0x59000 +#define OMAP4_GPIO5_REV 0x5b000 +#define OMAP4_GPIO6_REV 0x5d000 + +#define AM335X_GPIO0_REV 0x07000 +#define AM335X_GPIO1_REV 0x4C000 +#define AM335X_GPIO2_REV 0xAC000 +#define AM335X_GPIO3_REV 0xAE000 + static int ti_gpio_intr(void *arg); static int ti_gpio_detach(device_t); static int ti_gpio_pic_attach(struct ti_gpio_softc *sc); static int ti_gpio_pic_detach(struct ti_gpio_softc *sc); static u_int ti_first_gpio_bank(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_FIRST_GPIO_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_FIRST_GPIO_BANK); #endif } return (0); } static uint32_t ti_gpio_rev(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_GPIO_REV); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_GPIO_REV); #endif } return (0); } /** * Macros for driver mutex locking */ #define TI_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define TI_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define TI_GPIO_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ "ti_gpio", MTX_SPIN) #define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) /** * ti_gpio_read_4 - reads a 32-bit value from one of the GPIO registers * @sc: GPIO device context * @bank: The bank to read from * @off: The offset of a register from the GPIO register address range * * * RETURNS: * 32-bit value read from the register. */ static inline uint32_t ti_gpio_read_4(struct ti_gpio_softc *sc, bus_size_t off) { return (bus_read_4(sc->sc_mem_res, off)); } /** * ti_gpio_write_4 - writes a 32-bit value to one of the GPIO registers * @sc: GPIO device context * @bank: The bank to write to * @off: The offset of a register from the GPIO register address range * @val: The value to write into the register * * RETURNS: * nothing */ static inline void ti_gpio_write_4(struct ti_gpio_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res, off, val); } static inline void ti_gpio_intr_clr(struct ti_gpio_softc *sc, uint32_t mask) { /* We clear both set of registers. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_0, mask); ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_CLR_1, mask); } static inline void ti_gpio_intr_set(struct ti_gpio_softc *sc, uint32_t mask) { /* * On OMAP4 we unmask only the MPU interrupt and on AM335x we * also activate only the first interrupt. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_SET_0, mask); } static inline void ti_gpio_intr_ack(struct ti_gpio_softc *sc, uint32_t mask) { /* * Acknowledge the interrupt on both registers even if we use only * the first one. */ ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_0, mask); ti_gpio_write_4(sc, TI_GPIO_IRQSTATUS_1, mask); } static inline uint32_t ti_gpio_intr_status(struct ti_gpio_softc *sc) { uint32_t reg; /* Get the status from both registers. */ reg = ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_0); reg |= ti_gpio_read_4(sc, TI_GPIO_IRQSTATUS_1); return (reg); } static device_t ti_gpio_get_bus(device_t dev) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } /** * ti_gpio_pin_max - Returns the maximum number of GPIO pins * @dev: gpio device handle * @maxpin: pointer to a value that upon return will contain the maximum number * of pins in the device. * * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = PINS_PER_BANK - 1; return (0); } static int ti_gpio_valid_pin(struct ti_gpio_softc *sc, int pin) { if (pin >= sc->sc_maxpin || sc->sc_mem_res == NULL) return (EINVAL); return (0); } /** * ti_gpio_pin_getcaps - Gets the capabilities of a given pin * @dev: gpio device handle * @pin: the number of the pin * @caps: pointer to a value that upon return will contain the capabilities * * Currently all pins have the same capability, notably: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * - GPIO_INTR_LEVEL_LOW * - GPIO_INTR_LEVEL_HIGH * - GPIO_INTR_EDGE_RISING * - GPIO_INTR_EDGE_FALLING * - GPIO_INTR_EDGE_BOTH * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH); return (0); } /** * ti_gpio_pin_getflags - Gets the current flags of a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: upon return will contain the current flags of the pin * * Reads the current flags of a given pin, here we actually read the H/W * registers to determine the flags, rather than storing the value in the * setflags call. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Get the current pin state */ TI_GPIO_LOCK(sc); TI_GPIO_GET_FLAGS(dev, pin, flags); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getname - Gets the name of a given pin * @dev: gpio device handle * @pin: the number of the pin * @name: buffer to put the name in * * The driver simply calls the pins gpio_n, where 'n' is obviously the number * of the pin. * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set a very simple name */ snprintf(name, GPIOMAXNAME, "gpio_%u", pin); name[GPIOMAXNAME - 1] = '\0'; return (0); } /** * ti_gpio_pin_setflags - Sets the flags for a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: the flags to set * * The flags of the pin correspond to things like input/output mode, pull-ups, * pull-downs, etc. This driver doesn't support all flags, only the following: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct ti_gpio_softc *sc; uint32_t oe; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set the GPIO mode and state */ TI_GPIO_LOCK(sc); if (TI_GPIO_SET_FLAGS(dev, pin, flags) != 0) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* If configuring as an output set the "output enable" bit */ oe = ti_gpio_read_4(sc, TI_GPIO_OE); if (flags & GPIO_PIN_INPUT) oe |= TI_GPIO_MASK(pin); else oe &= ~TI_GPIO_MASK(pin); ti_gpio_write_4(sc, TI_GPIO_OE, oe); TI_GPIO_UNLOCK(sc); - + return (0); } /** * ti_gpio_pin_set - Sets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: non-zero value will drive the pin high, otherwise the pin is * driven low. * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ti_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); TI_GPIO_LOCK(sc); if (value == GPIO_PIN_LOW) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_get - Gets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: pointer to a value that upond return will contain the pin value * * The pin must be configured as an input pin beforehand, otherwise this * function will fail. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) { struct ti_gpio_softc *sc; uint32_t oe, reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* - * Return data from output latch when set as output and from the + * Return data from output latch when set as output and from the * input register otherwise. */ TI_GPIO_LOCK(sc); oe = ti_gpio_read_4(sc, TI_GPIO_OE); if (oe & TI_GPIO_MASK(pin)) reg = TI_GPIO_DATAIN; else reg = TI_GPIO_DATAOUT; val = ti_gpio_read_4(sc, reg); *value = (val & TI_GPIO_MASK(pin)) ? 1 : 0; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_toggle - Toggles a given GPIO pin * @dev: gpio device handle * @pin: the number of the pin * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_toggle(device_t dev, uint32_t pin) { struct ti_gpio_softc *sc; uint32_t reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Toggle the pin */ TI_GPIO_LOCK(sc); val = ti_gpio_read_4(sc, TI_GPIO_DATAOUT); if (val & TI_GPIO_MASK(pin)) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } static int ti_gpio_bank_init(device_t dev) { - int pin; + int pin, err; struct ti_gpio_softc *sc; uint32_t flags, reg_oe, reg_set, rev; - clk_ident_t clk; + uint64_t rev_address; sc = device_get_softc(dev); /* Enable the interface and functional clocks for the module. */ - clk = ti_hwmods_get_clock(dev); - if (clk == INVALID_CLK_IDENT) { - device_printf(dev, "failed to get device id based on ti,hwmods\n"); + rev_address = ti_sysc_get_rev_address(device_get_parent(dev)); + /* AM335x + * sc->sc_bank used in am335x/am335x_gpio.c and omap4/omap4_gpio.c */ + switch(ti_chip()) { +#ifdef SOC_OMAP4 + case CHIP_OMAP_4: + switch (rev_address) { + case OMAP4_GPIO1_REV: + sc->sc_bank = 0; + break; + case OMAP4_GPIO2_REV: + sc->sc_bank = 1; + break; + case OMAP4_GPIO3_REV: + sc->sc_bank = 2; + break; + case OMAP4_GPIO4_REV: + sc->sc_bank = 3; + break; + case OMAP4_GPIO5_REV: + sc->sc_bank = 4; + break; + case OMAP4_GPIO6_REV: + sc->sc_bank = 5; + break; + } +#endif +#ifdef SOC_TI_AM335X + case CHIP_AM335X: + switch (rev_address) { + case AM335X_GPIO0_REV: + sc->sc_bank = 0; + break; + case AM335X_GPIO1_REV: + sc->sc_bank = 1; + break; + case AM335X_GPIO2_REV: + sc->sc_bank = 2; + break; + case AM335X_GPIO3_REV: + sc->sc_bank = 3; + break; + } +#endif + } + err = ti_sysc_clock_enable(device_get_parent(dev)); + if (err) { + device_printf(dev, "Failed to enable clock\n"); return (EINVAL); } - sc->sc_bank = clk - GPIO1_CLK + ti_first_gpio_bank(); - ti_prcm_clk_enable(clk); - /* * Read the revision number of the module. TI don't publish the * actual revision numbers, so instead the values have been * determined by experimentation. */ - rev = ti_gpio_read_4(sc, TI_GPIO_REVISION); + rev = ti_gpio_read_4(sc, + ti_sysc_get_rev_address_offset_host(device_get_parent(dev))); /* Check the revision. */ if (rev != ti_gpio_rev()) { device_printf(dev, "Warning: could not determine the revision " "of GPIO module (revision:0x%08x)\n", rev); return (EINVAL); } /* Disable interrupts for all pins. */ ti_gpio_intr_clr(sc, 0xffffffff); /* Init OE register based on pads configuration. */ reg_oe = 0xffffffff; reg_set = 0; for (pin = 0; pin < PINS_PER_BANK; pin++) { TI_GPIO_GET_FLAGS(dev, pin, &flags); if (flags & GPIO_PIN_OUTPUT) { reg_oe &= ~(1UL << pin); if (flags & GPIO_PIN_PULLUP) reg_set |= (1UL << pin); } } ti_gpio_write_4(sc, TI_GPIO_OE, reg_oe); if (reg_set) ti_gpio_write_4(sc, TI_GPIO_SETDATAOUT, reg_set); return (0); } /** * ti_gpio_attach - attach function for the driver * @dev: gpio device handle * * Allocates and sets up the driver context for all GPIO banks. This function * expects the memory ranges and IRQs to already be allocated to the driver. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_attach(device_t dev) { struct ti_gpio_softc *sc; int err; sc = device_get_softc(dev); sc->sc_dev = dev; TI_GPIO_LOCK_INIT(sc); ti_gpio_pin_max(dev, &sc->sc_maxpin); sc->sc_maxpin++; sc->sc_mem_rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "Error: could not allocate mem resources\n"); ti_gpio_detach(dev); return (ENXIO); } sc->sc_irq_rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "Error: could not allocate irq resources\n"); ti_gpio_detach(dev); return (ENXIO); } /* * Register our interrupt filter for each of the IRQ resources. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc, &sc->sc_irq_hdl) != 0) { device_printf(dev, "WARNING: unable to register interrupt filter\n"); ti_gpio_detach(dev); return (ENXIO); } if (ti_gpio_pic_attach(sc) != 0) { device_printf(dev, "WARNING: unable to attach PIC\n"); ti_gpio_detach(dev); return (ENXIO); } /* We need to go through each block and ensure the clocks are running and * the module is enabled. It might be better to do this only when the * pins are configured which would result in less power used if the GPIO - * pins weren't used ... + * pins weren't used ... */ if (sc->sc_mem_res != NULL) { /* Initialize the GPIO module. */ err = ti_gpio_bank_init(dev); if (err != 0) { ti_gpio_detach(dev); return (err); } } sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { ti_gpio_detach(dev); return (ENXIO); } return (0); } /** * ti_gpio_detach - detach function for the driver * @dev: scm device handle * * Allocates and sets up the driver context, this simply entails creating a * bus mappings for the SCM register set. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_detach(device_t dev) { struct ti_gpio_softc *sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); /* Disable all interrupts */ if (sc->sc_mem_res != NULL) ti_gpio_intr_clr(sc, 0xffffffff); if (sc->sc_busdev != NULL) gpiobus_detach_bus(dev); if (sc->sc_isrcs != NULL) ti_gpio_pic_detach(sc); /* Release the memory and IRQ resources. */ if (sc->sc_irq_hdl) { bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdl); } if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res); TI_GPIO_LOCK_DESTROY(sc); return (0); } static inline void ti_gpio_rwreg_modify(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask, bool set_bits) { uint32_t value; value = ti_gpio_read_4(sc, reg); ti_gpio_write_4(sc, reg, set_bits ? value | mask : value & ~mask); } static inline void ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_clr(sc, tgi->tgi_mask); } static inline void ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_set(sc, tgi->tgi_mask); } static inline void ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi) { /* Writing a 0 has no effect. */ ti_gpio_intr_ack(sc, tgi->tgi_mask); } static inline bool ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi) { return (tgi->tgi_mode == GPIO_INTR_LEVEL_LOW || tgi->tgi_mode == GPIO_INTR_LEVEL_HIGH); } static int ti_gpio_intr(void *arg) { u_int irq; uint32_t reg; struct ti_gpio_softc *sc; struct trapframe *tf; struct ti_gpio_irqsrc *tgi; sc = (struct ti_gpio_softc *)arg; tf = curthread->td_intr_frame; reg = ti_gpio_intr_status(sc); for (irq = 0; irq < sc->sc_maxpin; irq++) { tgi = &sc->sc_isrcs[irq]; if ((reg & tgi->tgi_mask) == 0) continue; if (!ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) { ti_gpio_isrc_mask(sc, tgi); if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); } } return (FILTER_HANDLED); } static int ti_gpio_pic_attach(struct ti_gpio_softc *sc) { int error; uint32_t irq; const char *name; sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->sc_dev); for (irq = 0; irq < sc->sc_maxpin; irq++) { sc->sc_isrcs[irq].tgi_irq = irq; sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq); sc->sc_isrcs[irq].tgi_mode = GPIO_INTR_CONFORM; error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc, sc->sc_dev, 0, "%s,%u", name, irq); if (error != 0) return (error); /* XXX deregister ISRCs */ } if (intr_pic_register(sc->sc_dev, OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) return (ENXIO); return (0); } static int ti_gpio_pic_detach(struct ti_gpio_softc *sc) { /* * There has not been established any procedure yet * how to detach PIC from living system correctly. */ device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); return (EBUSY); } static void ti_gpio_pic_config_intr(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi, uint32_t mode) { TI_GPIO_LOCK(sc); ti_gpio_rwreg_modify(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask, mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); ti_gpio_rwreg_modify(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask, mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask, mode == GPIO_INTR_LEVEL_HIGH); ti_gpio_rwreg_modify(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask, mode == GPIO_INTR_LEVEL_LOW); tgi->tgi_mode = mode; TI_GPIO_UNLOCK(sc); } static void ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; ti_gpio_isrc_mask(sc, tgi); } static void ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; arm_irq_memory_barrier(tgi->tgi_irq); ti_gpio_isrc_unmask(sc, tgi); } static int ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, struct intr_map_data_fdt *daf, u_int *irqp, uint32_t *modep) { uint32_t mode; /* * The first cell is the interrupt number. * The second cell is used to specify flags: * bits[3:0] trigger type and level flags: * 1 = low-to-high edge triggered. * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. */ if (daf->ncells != 2 || daf->cells[0] >= sc->sc_maxpin) return (EINVAL); /* Only reasonable modes are supported. */ if (daf->cells[1] == 1) mode = GPIO_INTR_EDGE_RISING; else if (daf->cells[1] == 2) mode = GPIO_INTR_EDGE_FALLING; else if (daf->cells[1] == 3) mode = GPIO_INTR_EDGE_BOTH; else if (daf->cells[1] == 4) mode = GPIO_INTR_LEVEL_HIGH; else if (daf->cells[1] == 8) mode = GPIO_INTR_LEVEL_LOW; else return (EINVAL); *irqp = daf->cells[0]; if (modep != NULL) *modep = mode; return (0); } static int ti_gpio_pic_map_gpio(struct ti_gpio_softc *sc, struct intr_map_data_gpio *dag, u_int *irqp, uint32_t *modep) { uint32_t mode; if (dag->gpio_pin_num >= sc->sc_maxpin) return (EINVAL); mode = dag->gpio_intr_mode; if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && mode != GPIO_INTR_EDGE_BOTH) return (EINVAL); *irqp = dag->gpio_pin_num; if (modep != NULL) *modep = mode; return (0); } static int ti_gpio_pic_map(struct ti_gpio_softc *sc, struct intr_map_data *data, u_int *irqp, uint32_t *modep) { switch (data->type) { case INTR_MAP_DATA_FDT: return (ti_gpio_pic_map_fdt(sc, (struct intr_map_data_fdt *)data, irqp, modep)); case INTR_MAP_DATA_GPIO: return (ti_gpio_pic_map_gpio(sc, (struct intr_map_data_gpio *)data, irqp, modep)); default: return (ENOTSUP); } } static int ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { int error; u_int irq; struct ti_gpio_softc *sc = device_get_softc(dev); error = ti_gpio_pic_map(sc, data, &irq, NULL); if (error == 0) *isrcp = &sc->sc_isrcs[irq].tgi_isrc; return (error); } static void ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); } static void ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { ti_gpio_pic_enable_intr(dev, isrc); } static void ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; ti_gpio_isrc_mask(sc, tgi); if (ti_gpio_isrc_is_level(tgi)) ti_gpio_isrc_eoi(sc, tgi); } static int ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { u_int irq; uint32_t mode; struct ti_gpio_softc *sc; struct ti_gpio_irqsrc *tgi; if (data == NULL) return (ENOTSUP); sc = device_get_softc(dev); tgi = (struct ti_gpio_irqsrc *)isrc; /* Get and check config for an interrupt. */ if (ti_gpio_pic_map(sc, data, &irq, &mode) != 0 || tgi->tgi_irq != irq) return (EINVAL); /* * If this is a setup for another handler, * only check that its configuration match. */ if (isrc->isrc_handlers != 0) return (tgi->tgi_mode == mode ? 0 : EINVAL); ti_gpio_pic_config_intr(sc, tgi, mode); return (0); } static int ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { struct ti_gpio_softc *sc = device_get_softc(dev); struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc; if (isrc->isrc_handlers == 0) ti_gpio_pic_config_intr(sc, tgi, GPIO_INTR_CONFORM); return (0); } static phandle_t ti_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_gpio_methods[] = { DEVMETHOD(device_attach, ti_gpio_attach), DEVMETHOD(device_detach, ti_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, ti_gpio_get_bus), DEVMETHOD(gpio_pin_max, ti_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, ti_gpio_pic_disable_intr), DEVMETHOD(pic_enable_intr, ti_gpio_pic_enable_intr), DEVMETHOD(pic_map_intr, ti_gpio_pic_map_intr), DEVMETHOD(pic_setup_intr, ti_gpio_pic_setup_intr), DEVMETHOD(pic_teardown_intr, ti_gpio_pic_teardown_intr), DEVMETHOD(pic_post_filter, ti_gpio_pic_post_filter), DEVMETHOD(pic_post_ithread, ti_gpio_pic_post_ithread), DEVMETHOD(pic_pre_ithread, ti_gpio_pic_pre_ithread), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), {0, 0}, }; driver_t ti_gpio_driver = { "gpio", ti_gpio_methods, sizeof(struct ti_gpio_softc), }; Index: head/sys/arm/ti/ti_i2c.c =================================================================== --- head/sys/arm/ti/ti_i2c.c (revision 363699) +++ head/sys/arm/ti/ti_i2c.c (revision 363700) @@ -1,990 +1,978 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 Ben Gray . * Copyright (c) 2014 Luiz Otavio O Souza . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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. */ /** * Driver for the I2C module on the TI SoC. * * This driver is heavily based on the TWI driver for the AT91 (at91_twi.c). * * CAUTION: The I2Ci registers are limited to 16 bit and 8 bit data accesses, * 32 bit data access is not allowed and can corrupt register content. * * This driver currently doesn't use DMA for the transfer, although I hope to * incorporate that sometime in the future. The idea being that for transaction * larger than a certain size the DMA engine is used, for anything less the * normal interrupt/fifo driven option is used. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include #include #include #include #include "iicbus_if.h" /** * I2C device driver context, a pointer to this is stored in the device * driver structure. */ struct ti_i2c_softc { device_t sc_dev; - clk_ident_t clk_id; struct resource* sc_irq_res; struct resource* sc_mem_res; device_t sc_iicbus; void* sc_irq_h; struct mtx sc_mtx; struct iic_msg* sc_buffer; int sc_bus_inuse; int sc_buffer_pos; int sc_error; int sc_fifo_trsh; int sc_timeout; uint16_t sc_con_reg; uint16_t sc_rev; }; struct ti_i2c_clock_config { u_int frequency; /* Bus frequency in Hz */ uint8_t psc; /* Fast/Standard mode prescale divider */ uint8_t scll; /* Fast/Standard mode SCL low time */ uint8_t sclh; /* Fast/Standard mode SCL high time */ uint8_t hsscll; /* High Speed mode SCL low time */ uint8_t hssclh; /* High Speed mode SCL high time */ }; #if defined(SOC_OMAP4) /* * OMAP4 i2c bus clock is 96MHz / ((psc + 1) * (scll + 7 + sclh + 5)). * The prescaler values for 100KHz and 400KHz modes come from the table in the * OMAP4 TRM. The table doesn't list 1MHz; these values should give that speed. */ static struct ti_i2c_clock_config ti_omap4_i2c_clock_configs[] = { { 100000, 23, 13, 15, 0, 0}, { 400000, 9, 5, 7, 0, 0}, { 1000000, 3, 5, 7, 0, 0}, /* { 3200000, 1, 113, 115, 7, 10}, - HS mode */ { 0 /* Table terminator */ } }; #endif #if defined(SOC_TI_AM335X) /* * AM335x i2c bus clock is 48MHZ / ((psc + 1) * (scll + 7 + sclh + 5)) * In all cases we prescale the clock to 24MHz as recommended in the manual. */ static struct ti_i2c_clock_config ti_am335x_i2c_clock_configs[] = { { 100000, 1, 111, 117, 0, 0}, { 400000, 1, 23, 25, 0, 0}, { 1000000, 1, 5, 7, 0, 0}, { 0 /* Table terminator */ } }; #endif /** * Locking macros used throughout the driver */ #define TI_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define TI_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define TI_I2C_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "ti_i2c", MTX_DEF) #define TI_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) #define TI_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define TI_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) #ifdef DEBUG #define ti_i2c_dbg(_sc, fmt, args...) \ device_printf((_sc)->sc_dev, fmt, ##args) #else #define ti_i2c_dbg(_sc, fmt, args...) #endif /** * ti_i2c_read_2 - reads a 16-bit value from one of the I2C registers * @sc: I2C device context * @off: the byte offset within the register bank to read from. * * * LOCKING: * No locking required * * RETURNS: * 16-bit value read from the register. */ static inline uint16_t ti_i2c_read_2(struct ti_i2c_softc *sc, bus_size_t off) { return (bus_read_2(sc->sc_mem_res, off)); } /** * ti_i2c_write_2 - writes a 16-bit value to one of the I2C registers * @sc: I2C device context * @off: the byte offset within the register bank to read from. * @val: the value to write into the register * * LOCKING: * No locking required * * RETURNS: * 16-bit value read from the register. */ static inline void ti_i2c_write_2(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) { bus_write_2(sc->sc_mem_res, off, val); } static int ti_i2c_transfer_intr(struct ti_i2c_softc* sc, uint16_t status) { int amount, done, i; done = 0; amount = 0; /* Check for the error conditions. */ if (status & I2C_STAT_NACK) { /* No ACK from slave. */ ti_i2c_dbg(sc, "NACK\n"); ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_NACK); sc->sc_error = ENXIO; } else if (status & I2C_STAT_AL) { /* Arbitration lost. */ ti_i2c_dbg(sc, "Arbitration lost\n"); ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_AL); sc->sc_error = ENXIO; } /* Check if we have finished. */ if (status & I2C_STAT_ARDY) { /* Register access ready - transaction complete basically. */ ti_i2c_dbg(sc, "ARDY transaction complete\n"); if (sc->sc_error != 0 && sc->sc_buffer->flags & IIC_M_NOSTOP) { ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg | I2C_CON_STP); } ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_ARDY | I2C_STAT_RDR | I2C_STAT_RRDY | I2C_STAT_XDR | I2C_STAT_XRDY); return (1); } if (sc->sc_buffer->flags & IIC_M_RD) { /* Read some data. */ if (status & I2C_STAT_RDR) { /* * Receive draining interrupt - last data received. * The set FIFO threshold won't be reached to trigger * RRDY. */ ti_i2c_dbg(sc, "Receive draining interrupt\n"); /* * Drain the FIFO. Read the pending data in the FIFO. */ amount = sc->sc_buffer->len - sc->sc_buffer_pos; } else if (status & I2C_STAT_RRDY) { /* * Receive data ready interrupt - FIFO has reached the * set threshold. */ ti_i2c_dbg(sc, "Receive data ready interrupt\n"); amount = min(sc->sc_fifo_trsh, sc->sc_buffer->len - sc->sc_buffer_pos); } /* Read the bytes from the fifo. */ for (i = 0; i < amount; i++) sc->sc_buffer->buf[sc->sc_buffer_pos++] = (uint8_t)(ti_i2c_read_2(sc, I2C_REG_DATA) & 0xff); if (status & I2C_STAT_RDR) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RDR); if (status & I2C_STAT_RRDY) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RRDY); } else { /* Write some data. */ if (status & I2C_STAT_XDR) { /* * Transmit draining interrupt - FIFO level is below * the set threshold and the amount of data still to * be transferred won't reach the set FIFO threshold. */ ti_i2c_dbg(sc, "Transmit draining interrupt\n"); /* * Drain the TX data. Write the pending data in the * FIFO. */ amount = sc->sc_buffer->len - sc->sc_buffer_pos; } else if (status & I2C_STAT_XRDY) { /* * Transmit data ready interrupt - the FIFO level * is below the set threshold. */ ti_i2c_dbg(sc, "Transmit data ready interrupt\n"); amount = min(sc->sc_fifo_trsh, sc->sc_buffer->len - sc->sc_buffer_pos); } /* Write the bytes from the fifo. */ for (i = 0; i < amount; i++) ti_i2c_write_2(sc, I2C_REG_DATA, sc->sc_buffer->buf[sc->sc_buffer_pos++]); if (status & I2C_STAT_XDR) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XDR); if (status & I2C_STAT_XRDY) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XRDY); } return (done); } /** * ti_i2c_intr - interrupt handler for the I2C module * @dev: i2c device handle * * * * LOCKING: * Called from timer context * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ static void ti_i2c_intr(void *arg) { int done; struct ti_i2c_softc *sc; uint16_t events, status; sc = (struct ti_i2c_softc *)arg; TI_I2C_LOCK(sc); status = ti_i2c_read_2(sc, I2C_REG_STATUS); if (status == 0) { TI_I2C_UNLOCK(sc); return; } /* Save enabled interrupts. */ events = ti_i2c_read_2(sc, I2C_REG_IRQENABLE_SET); /* We only care about enabled interrupts. */ status &= events; done = 0; if (sc->sc_buffer != NULL) done = ti_i2c_transfer_intr(sc, status); else { ti_i2c_dbg(sc, "Transfer interrupt without buffer\n"); sc->sc_error = EINVAL; done = 1; } if (done) /* Wakeup the process that started the transaction. */ wakeup(sc); TI_I2C_UNLOCK(sc); } /** * ti_i2c_transfer - called to perform the transfer * @dev: i2c device handle * @msgs: the messages to send/receive * @nmsgs: the number of messages in the msgs array * * * LOCKING: * Internally locked * * RETURNS: * 0 on function succeeded * EINVAL if invalid message is passed as an arg */ static int ti_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int err, i, repstart, timeout; struct ti_i2c_softc *sc; uint16_t reg; sc = device_get_softc(dev); TI_I2C_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_bus_inuse == 1) mtx_sleep(sc, &sc->sc_mtx, 0, "i2cbuswait", 0); /* Now we have control over the I2C controller. */ sc->sc_bus_inuse = 1; err = 0; repstart = 0; for (i = 0; i < nmsgs; i++) { sc->sc_buffer = &msgs[i]; sc->sc_buffer_pos = 0; sc->sc_error = 0; /* Zero byte transfers aren't allowed. */ if (sc->sc_buffer == NULL || sc->sc_buffer->buf == NULL || sc->sc_buffer->len == 0) { err = EINVAL; break; } /* Check if the i2c bus is free. */ if (repstart == 0) { /* * On repeated start we send the START condition while * the bus _is_ busy. */ timeout = 0; while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) { if (timeout++ > 100) { err = EBUSY; goto out; } DELAY(1000); } timeout = 0; } else repstart = 0; if (sc->sc_buffer->flags & IIC_M_NOSTOP) repstart = 1; /* Set the slave address. */ ti_i2c_write_2(sc, I2C_REG_SA, msgs[i].slave >> 1); /* Write the data length. */ ti_i2c_write_2(sc, I2C_REG_CNT, sc->sc_buffer->len); /* Clear the RX and the TX FIFO. */ reg = ti_i2c_read_2(sc, I2C_REG_BUF); reg |= I2C_BUF_RXFIFO_CLR | I2C_BUF_TXFIFO_CLR; ti_i2c_write_2(sc, I2C_REG_BUF, reg); reg = sc->sc_con_reg | I2C_CON_STT; if (repstart == 0) reg |= I2C_CON_STP; if ((sc->sc_buffer->flags & IIC_M_RD) == 0) reg |= I2C_CON_TRX; ti_i2c_write_2(sc, I2C_REG_CON, reg); /* Wait for an event. */ err = mtx_sleep(sc, &sc->sc_mtx, 0, "i2ciowait", sc->sc_timeout); if (err == 0) err = sc->sc_error; if (err) break; } out: if (timeout == 0) { while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) { if (timeout++ > 100) break; DELAY(1000); } } /* Put the controller in master mode again. */ if ((ti_i2c_read_2(sc, I2C_REG_CON) & I2C_CON_MST) == 0) ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); sc->sc_buffer = NULL; sc->sc_bus_inuse = 0; /* Wake up the processes that are waiting for the bus. */ wakeup(sc); TI_I2C_UNLOCK(sc); return (err); } static int ti_i2c_reset(struct ti_i2c_softc *sc, u_char speed) { int timeout; struct ti_i2c_clock_config *clkcfg; u_int busfreq; uint16_t fifo_trsh, reg, scll, sclh; switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: clkcfg = ti_omap4_i2c_clock_configs; break; #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: clkcfg = ti_am335x_i2c_clock_configs; break; #endif default: panic("Unknown TI SoC, unable to reset the i2c"); } /* * If we haven't attached the bus yet, just init at the default slow * speed. This lets us get the hardware initialized enough to attach * the bus which is where the real speed configuration is handled. After * the bus is attached, get the configured speed from it. Search the * configuration table for the best speed we can do that doesn't exceed * the requested speed. */ if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); for (;;) { if (clkcfg[1].frequency == 0 || clkcfg[1].frequency > busfreq) break; clkcfg++; } /* * 23.1.4.3 - HS I2C Software Reset * From OMAP4 TRM at page 4068. * * 1. Ensure that the module is disabled. */ sc->sc_con_reg = 0; ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* 2. Issue a softreset to the controller. */ bus_write_2(sc->sc_mem_res, I2C_REG_SYSC, I2C_REG_SYSC_SRST); /* * 3. Enable the module. * The I2Ci.I2C_SYSS[0] RDONE bit is asserted only after the module * is enabled by setting the I2Ci.I2C_CON[15] I2C_EN bit to 1. */ ti_i2c_write_2(sc, I2C_REG_CON, I2C_CON_I2C_EN); /* 4. Wait for the software reset to complete. */ timeout = 0; while ((ti_i2c_read_2(sc, I2C_REG_SYSS) & I2C_SYSS_RDONE) == 0) { if (timeout++ > 100) return (EBUSY); DELAY(100); } /* * Disable the I2C controller once again, now that the reset has * finished. */ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* * The following sequence is taken from the OMAP4 TRM at page 4077. * * 1. Enable the functional and interface clocks (see Section * 23.1.5.1.1.1.1). Done at ti_i2c_activate(). * * 2. Program the prescaler to obtain an approximately 12MHz internal * sampling clock (I2Ci_INTERNAL_CLK) by programming the * corresponding value in the I2Ci.I2C_PSC[3:0] PSC field. * This value depends on the frequency of the functional clock * (I2Ci_FCLK). Because this frequency is 96MHz, the * I2Ci.I2C_PSC[7:0] PSC field value is 0x7. */ ti_i2c_write_2(sc, I2C_REG_PSC, clkcfg->psc); /* * 3. Program the I2Ci.I2C_SCLL[7:0] SCLL and I2Ci.I2C_SCLH[7:0] SCLH * bit fields to obtain a bit rate of 100 Kbps, 400 Kbps or 1Mbps. * These values depend on the internal sampling clock frequency * (see Table 23-8). */ scll = clkcfg->scll & I2C_SCLL_MASK; sclh = clkcfg->sclh & I2C_SCLH_MASK; /* * 4. (Optional) Program the I2Ci.I2C_SCLL[15:8] HSSCLL and * I2Ci.I2C_SCLH[15:8] HSSCLH fields to obtain a bit rate of * 400K bps or 3.4M bps (for the second phase of HS mode). These * values depend on the internal sampling clock frequency (see * Table 23-8). * * 5. (Optional) If a bit rate of 3.4M bps is used and the bus line * capacitance exceeds 45 pF, (see Section 18.4.8, PAD Functional * Multiplexing and Configuration). */ switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: if ((clkcfg->hsscll + clkcfg->hssclh) > 0) { scll |= clkcfg->hsscll << I2C_HSSCLL_SHIFT; sclh |= clkcfg->hssclh << I2C_HSSCLH_SHIFT; sc->sc_con_reg |= I2C_CON_OPMODE_HS; } break; #endif } /* Write the selected bit rate. */ ti_i2c_write_2(sc, I2C_REG_SCLL, scll); ti_i2c_write_2(sc, I2C_REG_SCLH, sclh); /* * 6. Configure the Own Address of the I2C controller by storing it in * the I2Ci.I2C_OA0 register. Up to four Own Addresses can be * programmed in the I2Ci.I2C_OAi registers (where i = 0, 1, 2, 3) * for each I2C controller. * * Note: For a 10-bit address, set the corresponding expand Own Address * bit in the I2Ci.I2C_CON register. * * Driver currently always in single master mode so ignore this step. */ /* * 7. Set the TX threshold (in transmitter mode) and the RX threshold * (in receiver mode) by setting the I2Ci.I2C_BUF[5:0]XTRSH field to * (TX threshold - 1) and the I2Ci.I2C_BUF[13:8]RTRSH field to (RX * threshold - 1), where the TX and RX thresholds are greater than * or equal to 1. * * The threshold is set to 5 for now. */ fifo_trsh = (sc->sc_fifo_trsh - 1) & I2C_BUF_TRSH_MASK; reg = fifo_trsh | (fifo_trsh << I2C_BUF_RXTRSH_SHIFT); ti_i2c_write_2(sc, I2C_REG_BUF, reg); /* * 8. Take the I2C controller out of reset by setting the * I2Ci.I2C_CON[15] I2C_EN bit to 1. * * 23.1.5.1.1.1.2 - Initialize the I2C Controller * * To initialize the I2C controller, perform the following steps: * * 1. Configure the I2Ci.I2C_CON register: * . For master or slave mode, set the I2Ci.I2C_CON[10] MST bit * (0: slave, 1: master). * . For transmitter or receiver mode, set the I2Ci.I2C_CON[9] TRX * bit (0: receiver, 1: transmitter). */ /* Enable the I2C controller in master mode. */ sc->sc_con_reg |= I2C_CON_I2C_EN | I2C_CON_MST; ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* * 2. If using an interrupt to transmit/receive data, set the * corresponding bit in the I2Ci.I2C_IE register (the I2Ci.I2C_IE[4] * XRDY_IE bit for the transmit interrupt, the I2Ci.I2C_IE[3] RRDY * bit for the receive interrupt). */ /* Set the interrupts we want to be notified. */ reg = I2C_IE_XDR | /* Transmit draining interrupt. */ I2C_IE_XRDY | /* Transmit Data Ready interrupt. */ I2C_IE_RDR | /* Receive draining interrupt. */ I2C_IE_RRDY | /* Receive Data Ready interrupt. */ I2C_IE_ARDY | /* Register Access Ready interrupt. */ I2C_IE_NACK | /* No Acknowledgment interrupt. */ I2C_IE_AL; /* Arbitration lost interrupt. */ /* Enable the interrupts. */ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_SET, reg); /* * 3. If using DMA to receive/transmit data, set to 1 the corresponding * bit in the I2Ci.I2C_BUF register (the I2Ci.I2C_BUF[15] RDMA_EN * bit for the receive DMA channel, the I2Ci.I2C_BUF[7] XDMA_EN bit * for the transmit DMA channel). * * Not using DMA for now, so ignore this. */ return (0); } static int ti_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct ti_i2c_softc *sc; int err; sc = device_get_softc(dev); TI_I2C_LOCK(sc); err = ti_i2c_reset(sc, speed); TI_I2C_UNLOCK(sc); if (err) return (err); return (IIC_ENOADDR); } static int ti_i2c_activate(device_t dev) { int err; struct ti_i2c_softc *sc; sc = (struct ti_i2c_softc*)device_get_softc(dev); /* * 1. Enable the functional and interface clocks (see Section * 23.1.5.1.1.1.1). */ - err = ti_prcm_clk_enable(sc->clk_id); + err = ti_sysc_clock_enable(device_get_parent(dev)); if (err) return (err); return (ti_i2c_reset(sc, IIC_UNKNOWN)); } /** * ti_i2c_deactivate - deactivates the controller and releases resources * @dev: i2c device handle * * * * LOCKING: * Assumed called in an atomic context. * * RETURNS: * nothing */ static void ti_i2c_deactivate(device_t dev) { struct ti_i2c_softc *sc = device_get_softc(dev); /* Disable the controller - cancel all transactions. */ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_CLR, 0xffff); ti_i2c_write_2(sc, I2C_REG_STATUS, 0xffff); ti_i2c_write_2(sc, I2C_REG_CON, 0); /* Release the interrupt handler. */ if (sc->sc_irq_h != NULL) { bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); sc->sc_irq_h = NULL; } /* Unmap the I2C controller registers. */ if (sc->sc_mem_res != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); sc->sc_mem_res = NULL; } /* Release the IRQ resource. */ if (sc->sc_irq_res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } /* Finally disable the functional and interface clocks. */ - ti_prcm_clk_disable(sc->clk_id); + ti_sysc_clock_disable(device_get_parent(dev)); } static int ti_i2c_sysctl_clk(SYSCTL_HANDLER_ARGS) { int clk, psc, sclh, scll; struct ti_i2c_softc *sc; sc = arg1; TI_I2C_LOCK(sc); /* Get the system prescaler value. */ psc = (int)ti_i2c_read_2(sc, I2C_REG_PSC) + 1; /* Get the bitrate. */ scll = (int)ti_i2c_read_2(sc, I2C_REG_SCLL) & I2C_SCLL_MASK; sclh = (int)ti_i2c_read_2(sc, I2C_REG_SCLH) & I2C_SCLH_MASK; clk = I2C_CLK / psc / (scll + 7 + sclh + 5); TI_I2C_UNLOCK(sc); return (sysctl_handle_int(oidp, &clk, 0, req)); } static int ti_i2c_sysctl_timeout(SYSCTL_HANDLER_ARGS) { struct ti_i2c_softc *sc; unsigned int val; int err; sc = arg1; /* * MTX_DEF lock can't be held while doing uimove in * sysctl_handle_int */ TI_I2C_LOCK(sc); val = sc->sc_timeout; TI_I2C_UNLOCK(sc); err = sysctl_handle_int(oidp, &val, 0, req); /* Write request? */ if ((err == 0) && (req->newptr != NULL)) { TI_I2C_LOCK(sc); sc->sc_timeout = val; TI_I2C_UNLOCK(sc); } return (err); } static int ti_i2c_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,omap4-i2c")) return (ENXIO); device_set_desc(dev, "TI I2C Controller"); return (0); } static int ti_i2c_attach(device_t dev) { int err, rid; - phandle_t node; struct ti_i2c_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; uint16_t fifosz; sc = device_get_softc(dev); sc->sc_dev = dev; - /* Get the i2c device id from FDT. */ - node = ofw_bus_get_node(dev); - /* i2c ti,hwmods bindings is special: it start with index 1 */ - sc->clk_id = ti_hwmods_get_clock(dev); - if (sc->clk_id == INVALID_CLK_IDENT) { - device_printf(dev, "failed to get device id using ti,hwmod\n"); - return (ENXIO); - } - /* Get the memory resource for the register mapping. */ rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "Cannot map registers.\n"); return (ENXIO); } /* Allocate our IRQ resource. */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "Cannot allocate interrupt.\n"); return (ENXIO); } TI_I2C_LOCK_INIT(sc); /* First of all, we _must_ activate the H/W. */ err = ti_i2c_activate(dev); if (err) { device_printf(dev, "ti_i2c_activate failed\n"); goto out; } /* Read the version number of the I2C module */ sc->sc_rev = ti_i2c_read_2(sc, I2C_REG_REVNB_HI) & 0xff; /* Get the fifo size. */ fifosz = ti_i2c_read_2(sc, I2C_REG_BUFSTAT); fifosz >>= I2C_BUFSTAT_FIFODEPTH_SHIFT; fifosz &= I2C_BUFSTAT_FIFODEPTH_MASK; device_printf(dev, "I2C revision %d.%d FIFO size: %d bytes\n", sc->sc_rev >> 4, sc->sc_rev & 0xf, 8 << fifosz); /* Set the FIFO threshold to 5 for now. */ sc->sc_fifo_trsh = 5; /* Set I2C bus timeout */ sc->sc_timeout = 5*hz; ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "i2c_clock", CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, ti_i2c_sysctl_clk, "IU", "I2C bus clock"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "i2c_timeout", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, ti_i2c_sysctl_timeout, "IU", "I2C bus timeout (in ticks)"); /* Activate the interrupt. */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_i2c_intr, sc, &sc->sc_irq_h); if (err) goto out; /* Attach the iicbus. */ if ((sc->sc_iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { device_printf(dev, "could not allocate iicbus instance\n"); err = ENXIO; goto out; } /* Probe and attach the iicbus when interrupts are available. */ err = bus_delayed_attach_children(dev); out: if (err) { ti_i2c_deactivate(dev); TI_I2C_LOCK_DESTROY(sc); } return (err); } static int ti_i2c_detach(device_t dev) { struct ti_i2c_softc *sc; int rv; sc = device_get_softc(dev); if ((rv = bus_generic_detach(dev)) != 0) { device_printf(dev, "cannot detach child devices\n"); return (rv); } if (sc->sc_iicbus && (rv = device_delete_child(dev, sc->sc_iicbus)) != 0) return (rv); ti_i2c_deactivate(dev); TI_I2C_LOCK_DESTROY(sc); return (0); } static phandle_t ti_i2c_get_node(device_t bus, device_t dev) { /* Share controller node with iibus device. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_i2c_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_i2c_probe), DEVMETHOD(device_attach, ti_i2c_attach), DEVMETHOD(device_detach, ti_i2c_detach), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, ti_i2c_get_node), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, ti_i2c_iicbus_reset), DEVMETHOD(iicbus_transfer, ti_i2c_transfer), DEVMETHOD_END }; static driver_t ti_i2c_driver = { "iichb", ti_i2c_methods, sizeof(struct ti_i2c_softc), }; static devclass_t ti_i2c_devclass; DRIVER_MODULE(ti_iic, simplebus, ti_i2c_driver, ti_i2c_devclass, 0, 0); DRIVER_MODULE(iicbus, ti_iic, iicbus_driver, iicbus_devclass, 0, 0); -MODULE_DEPEND(ti_iic, ti_prcm, 1, 1, 1); +MODULE_DEPEND(ti_iic, ti_sysc, 1, 1, 1); MODULE_DEPEND(ti_iic, iicbus, 1, 1, 1); Index: head/sys/arm/ti/ti_mbox.c =================================================================== --- head/sys/arm/ti/ti_mbox.c (revision 363699) +++ head/sys/arm/ti/ti_mbox.c (revision 363700) @@ -1,265 +1,268 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Rui Paulo * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "mbox_if.h" #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_mbox_probe; static device_attach_t ti_mbox_attach; static device_detach_t ti_mbox_detach; static void ti_mbox_intr(void *); static int ti_mbox_read(device_t, int, uint32_t *); static int ti_mbox_write(device_t, int, uint32_t); struct ti_mbox_softc { struct mtx sc_mtx; struct resource *sc_mem_res; struct resource *sc_irq_res; void *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; }; #define TI_MBOX_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define TI_MBOX_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) static device_method_t ti_mbox_methods[] = { DEVMETHOD(device_probe, ti_mbox_probe), DEVMETHOD(device_attach, ti_mbox_attach), DEVMETHOD(device_detach, ti_mbox_detach), DEVMETHOD(mbox_read, ti_mbox_read), DEVMETHOD(mbox_write, ti_mbox_write), DEVMETHOD_END }; static driver_t ti_mbox_driver = { "ti_mbox", ti_mbox_methods, sizeof(struct ti_mbox_softc) }; static devclass_t ti_mbox_devclass; DRIVER_MODULE(ti_mbox, simplebus, ti_mbox_driver, ti_mbox_devclass, 0, 0); +MODULE_DEPEND(ti_mbox, ti_sysc, 1, 1, 1); static __inline uint32_t ti_mbox_reg_read(struct ti_mbox_softc *sc, uint16_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_mbox_reg_write(struct ti_mbox_softc *sc, uint16_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static int ti_mbox_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,omap4-mailbox")) { device_set_desc(dev, "TI System Mailbox"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_mbox_attach(device_t dev) { struct ti_mbox_softc *sc; int rid, delay, chan; uint32_t rev, sysconfig; - if (ti_prcm_clk_enable(MAILBOX0_CLK) != 0) { + if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) { device_printf(dev, "could not enable MBOX clock\n"); return (ENXIO); } + sc = device_get_softc(dev); rid = 0; mtx_init(&sc->sc_mtx, "TI mbox", NULL, MTX_DEF); sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); ti_mbox_detach(dev); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_mbox_intr, sc, &sc->sc_intr) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_mbox_detach(dev); return (ENXIO); } /* * Reset the controller. */ sysconfig = ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG); DPRINTF("initial sysconfig %d\n", sysconfig); sysconfig |= TI_MBOX_SYSCONFIG_SOFTRST; delay = 100; while (ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) & TI_MBOX_SYSCONFIG_SOFTRST) { delay--; DELAY(10); } if (delay == 0) { device_printf(dev, "controller reset failed\n"); ti_mbox_detach(dev); return (ENXIO); } /* * Enable smart idle mode. */ ti_mbox_reg_write(sc, TI_MBOX_SYSCONFIG, ti_mbox_reg_read(sc, TI_MBOX_SYSCONFIG) | TI_MBOX_SYSCONFIG_SMARTIDLE); - rev = ti_mbox_reg_read(sc, TI_MBOX_REVISION); + rev = ti_mbox_reg_read(sc, + ti_sysc_get_rev_address_offset_host(device_get_parent(dev))); DPRINTF("rev %d\n", rev); device_printf(dev, "revision %d.%d\n", (rev >> 8) & 0x4, rev & 0x40); /* * Enable message interrupts. */ for (chan = 0; chan < 8; chan++) ti_mbox_reg_write(sc, TI_MBOX_IRQENABLE_SET(chan), 1); return (0); } static int ti_mbox_detach(device_t dev) { struct ti_mbox_softc *sc; sc = device_get_softc(dev); if (sc->sc_intr) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); return (0); } static void ti_mbox_intr(void *arg) { struct ti_mbox_softc *sc; sc = arg; DPRINTF("interrupt %p", sc); } static int ti_mbox_read(device_t dev, int chan, uint32_t *data) { struct ti_mbox_softc *sc; if (chan < 0 || chan > 7) return (EINVAL); sc = device_get_softc(dev); return (ti_mbox_reg_read(sc, TI_MBOX_MESSAGE(chan))); } static int ti_mbox_write(device_t dev, int chan, uint32_t data) { int limit = 500; struct ti_mbox_softc *sc; if (chan < 0 || chan > 7) return (EINVAL); sc = device_get_softc(dev); TI_MBOX_LOCK(sc); /* XXX implement interrupt method */ while (ti_mbox_reg_read(sc, TI_MBOX_FIFOSTATUS(chan)) == 1 && limit--) { DELAY(10); } if (limit == 0) { device_printf(dev, "FIFOSTAUS%d stuck\n", chan); TI_MBOX_UNLOCK(sc); return (EAGAIN); } ti_mbox_reg_write(sc, TI_MBOX_MESSAGE(chan), data); return (0); } Index: head/sys/arm/ti/ti_omap4_cm.c =================================================================== --- head/sys/arm/ti/ti_omap4_cm.c (nonexistent) +++ head/sys/arm/ti/ti_omap4_cm.c (revision 363700) @@ -0,0 +1,151 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Emmanuel Vadot + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * Based on sys/arm/ti/ti_sysc.c + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +static struct ofw_compat_data compat_data[] = { + { "ti,omap4-cm", 1 }, + { NULL, 0 } +}; + +struct ti_omap4_cm_softc { + struct simplebus_softc sc; + device_t dev; +}; + +uint64_t +ti_omap4_cm_get_simplebus_base_host(device_t dev) { + struct ti_omap4_cm_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc.nranges == 0) + return (0); + + return (sc->sc.ranges[0].host); +} + +static int ti_omap4_cm_probe(device_t dev); +static int ti_omap4_cm_attach(device_t dev); +static int ti_omap4_cm_detach(device_t dev); + +static int +ti_omap4_cm_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI OMAP4-CM"); + if (!bootverbose) + device_quiet(dev); + + return (BUS_PROBE_DEFAULT); +} + +static int +ti_omap4_cm_attach(device_t dev) +{ + struct ti_omap4_cm_softc *sc; + device_t cdev; + phandle_t node, child; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + simplebus_init(dev, node); + if (simplebus_fill_ranges(node, &sc->sc) < 0) { + device_printf(dev, "could not get ranges\n"); + return (ENXIO); + } + + bus_generic_probe(sc->dev); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } + + return (bus_generic_attach(dev)); +} + +static int +ti_omap4_cm_detach(device_t dev) +{ + return (EBUSY); +} + +static device_method_t ti_omap4_cm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ti_omap4_cm_probe), + DEVMETHOD(device_attach, ti_omap4_cm_attach), + DEVMETHOD(device_detach, ti_omap4_cm_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ti_omap4_cm, ti_omap4_cm_driver, ti_omap4_cm_methods, + sizeof(struct ti_omap4_cm_softc), simplebus_driver); + +static devclass_t ti_omap4_cm_devclass; + +EARLY_DRIVER_MODULE(ti_omap4_cm, simplebus, ti_omap4_cm_driver, +ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); + +EARLY_DRIVER_MODULE(ti_omap4_cm, ofwbus, ti_omap4_cm_driver, +ti_omap4_cm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); + Property changes on: head/sys/arm/ti/ti_omap4_cm.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_omap4_cm.h =================================================================== --- head/sys/arm/ti/ti_omap4_cm.h (nonexistent) +++ head/sys/arm/ti/ti_omap4_cm.h (revision 363700) @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +#ifndef __TI_OMAP4_CM__ +#define __TI_OMAP4_CM__ + +uint64_t ti_omap4_cm_get_simplebus_base_host(device_t dev); + +#endif /* __TI_OMAP4_CM__ */ Property changes on: head/sys/arm/ti/ti_omap4_cm.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_pinmux.c =================================================================== --- head/sys/arm/ti/ti_pinmux.c (revision 363699) +++ head/sys/arm/ti/ti_pinmux.c (revision 363700) @@ -1,461 +1,463 @@ /* * Copyright (c) 2010 * Ben Gray . * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Ben Gray. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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. */ /** * Exposes pinmux module to pinctrl-compatible interface */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ti_pinmux.h" struct pincfg { uint32_t reg; uint32_t conf; }; static struct resource_spec ti_pinmux_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ { -1, 0 } }; static struct ti_pinmux_softc *ti_pinmux_sc; #define ti_pinmux_read_2(sc, reg) \ bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) #define ti_pinmux_write_2(sc, reg, val) \ bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) #define ti_pinmux_read_4(sc, reg) \ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) #define ti_pinmux_write_4(sc, reg, val) \ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) /** * ti_padconf_devmap - Array of pins, should be defined one per SoC * * This array is typically defined in one of the targeted *_scm_pinumx.c * files and is specific to the given SoC platform. Each entry in the array * corresponds to an individual pin. */ static const struct ti_pinmux_device *ti_pinmux_dev; /** * ti_pinmux_padconf_from_name - searches the list of pads and returns entry * with matching ball name. * @ballname: the name of the ball * * RETURNS: * A pointer to the matching padconf or NULL if the ball wasn't found. */ static const struct ti_pinmux_padconf* ti_pinmux_padconf_from_name(const char *ballname) { const struct ti_pinmux_padconf *padconf; padconf = ti_pinmux_dev->padconf; while (padconf->ballname != NULL) { if (strcmp(ballname, padconf->ballname) == 0) return(padconf); padconf++; } return (NULL); } /** * ti_pinmux_padconf_set_internal - sets the muxmode and state for a pad/pin * @padconf: pointer to the pad structure * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? * * * LOCKING: * Internally locks it's own context. * * RETURNS: * 0 on success. * EINVAL if pin requested is outside valid range or already in use. */ static int ti_pinmux_padconf_set_internal(struct ti_pinmux_softc *sc, const struct ti_pinmux_padconf *padconf, const char *muxmode, unsigned int state) { unsigned int mode; uint16_t reg_val; /* populate the new value for the PADCONF register */ reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask); /* find the new mode requested */ for (mode = 0; mode < 8; mode++) { if ((padconf->muxmodes[mode] != NULL) && (strcmp(padconf->muxmodes[mode], muxmode) == 0)) { break; } } /* couldn't find the mux mode */ if (mode >= 8) { printf("Invalid mode \"%s\"\n", muxmode); return (EINVAL); } /* set the mux mode */ reg_val |= (uint16_t)(mode & ti_pinmux_dev->padconf_muxmode_mask); if (bootverbose) device_printf(sc->sc_dev, "setting internal %x for %s\n", reg_val, muxmode); /* write the register value (16-bit writes) */ ti_pinmux_write_2(sc, padconf->reg_off, reg_val); return (0); } /** * ti_pinmux_padconf_set - sets the muxmode and state for a pad/pin * @padname: the name of the pad, i.e. "c12" * @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx" * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? * * * LOCKING: * Internally locks it's own context. * * RETURNS: * 0 on success. * EINVAL if pin requested is outside valid range or already in use. */ int ti_pinmux_padconf_set(const char *padname, const char *muxmode, unsigned int state) { const struct ti_pinmux_padconf *padconf; if (!ti_pinmux_sc) return (ENXIO); /* find the pin in the devmap */ padconf = ti_pinmux_padconf_from_name(padname); if (padconf == NULL) return (EINVAL); return (ti_pinmux_padconf_set_internal(ti_pinmux_sc, padconf, muxmode, state)); } /** * ti_pinmux_padconf_get - gets the muxmode and state for a pad/pin * @padname: the name of the pad, i.e. "c12" * @muxmode: upon return will contain the name of the muxmode of the pin * @state: upon return will contain the state of the pad/pin * * * LOCKING: * Internally locks it's own context. * * RETURNS: * 0 on success. * EINVAL if pin requested is outside valid range or already in use. */ int ti_pinmux_padconf_get(const char *padname, const char **muxmode, unsigned int *state) { const struct ti_pinmux_padconf *padconf; uint16_t reg_val; if (!ti_pinmux_sc) return (ENXIO); /* find the pin in the devmap */ padconf = ti_pinmux_padconf_from_name(padname); if (padconf == NULL) return (EINVAL); /* read the register value (16-bit reads) */ reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off); /* save the state */ if (state) *state = (reg_val & ti_pinmux_dev->padconf_sate_mask); /* save the mode */ if (muxmode) *muxmode = padconf->muxmodes[(reg_val & ti_pinmux_dev->padconf_muxmode_mask)]; return (0); } /** * ti_pinmux_padconf_set_gpiomode - converts a pad to GPIO mode. * @gpio: the GPIO pin number (0-195) * @state: the state to put the pad/pin in, i.e. PADCONF_PIN_??? * * * * LOCKING: * Internally locks it's own context. * * RETURNS: * 0 on success. * EINVAL if pin requested is outside valid range or already in use. */ int ti_pinmux_padconf_set_gpiomode(uint32_t gpio, unsigned int state) { const struct ti_pinmux_padconf *padconf; uint16_t reg_val; if (!ti_pinmux_sc) return (ENXIO); /* find the gpio pin in the padconf array */ padconf = ti_pinmux_dev->padconf; while (padconf->ballname != NULL) { if (padconf->gpio_pin == gpio) break; padconf++; } if (padconf->ballname == NULL) return (EINVAL); /* populate the new value for the PADCONF register */ reg_val = (uint16_t)(state & ti_pinmux_dev->padconf_sate_mask); /* set the mux mode */ reg_val |= (uint16_t)(padconf->gpio_mode & ti_pinmux_dev->padconf_muxmode_mask); /* write the register value (16-bit writes) */ ti_pinmux_write_2(ti_pinmux_sc, padconf->reg_off, reg_val); return (0); } /** * ti_pinmux_padconf_get_gpiomode - gets the current GPIO mode of the pin * @gpio: the GPIO pin number (0-195) * @state: upon return will contain the state * * * * LOCKING: * Internally locks it's own context. * * RETURNS: * 0 on success. * EINVAL if pin requested is outside valid range or not configured as GPIO. */ int ti_pinmux_padconf_get_gpiomode(uint32_t gpio, unsigned int *state) { const struct ti_pinmux_padconf *padconf; uint16_t reg_val; if (!ti_pinmux_sc) return (ENXIO); /* find the gpio pin in the padconf array */ padconf = ti_pinmux_dev->padconf; while (padconf->ballname != NULL) { if (padconf->gpio_pin == gpio) break; padconf++; } if (padconf->ballname == NULL) return (EINVAL); /* read the current register settings */ reg_val = ti_pinmux_read_2(ti_pinmux_sc, padconf->reg_off); /* check to make sure the pins is configured as GPIO in the first state */ if ((reg_val & ti_pinmux_dev->padconf_muxmode_mask) != padconf->gpio_mode) return (EINVAL); /* read and store the reset of the state, i.e. pull-up, pull-down, etc */ if (state) *state = (reg_val & ti_pinmux_dev->padconf_sate_mask); return (0); } static int ti_pinmux_configure_pins(device_t dev, phandle_t cfgxref) { struct pincfg *cfgtuples, *cfg; phandle_t cfgnode; int i, ntuples; static struct ti_pinmux_softc *sc; sc = device_get_softc(dev); cfgnode = OF_node_from_xref(cfgxref); ntuples = OF_getencprop_alloc_multi(cfgnode, "pinctrl-single,pins", sizeof(*cfgtuples), (void **)&cfgtuples); if (ntuples < 0) return (ENOENT); if (ntuples == 0) return (0); /* Empty property is not an error. */ for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) { if (bootverbose) { char name[32]; OF_getprop(cfgnode, "name", &name, sizeof(name)); printf("%16s: muxreg 0x%04x muxval 0x%02x\n", name, cfg->reg, cfg->conf); } /* write the register value (16-bit writes) */ ti_pinmux_write_2(sc, cfg->reg, cfg->conf); } OF_prop_free(cfgtuples); return (0); } /* * Device part of OMAP SCM driver */ static int ti_pinmux_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "pinctrl-single")) return (ENXIO); if (ti_pinmux_sc) { printf("%s: multiple pinctrl modules in device tree data, ignoring\n", __func__); return (EEXIST); } switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: ti_pinmux_dev = &omap4_pinmux_dev; break; #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: ti_pinmux_dev = &ti_am335x_pinmux_dev; break; #endif default: printf("Unknown CPU in pinmux\n"); return (ENXIO); } device_set_desc(dev, "TI Pinmux Module"); return (BUS_PROBE_DEFAULT); } /** * ti_pinmux_attach - attaches the pinmux to the simplebus * @dev: new device * * RETURNS * Zero on success or ENXIO if an error occuried. */ static int ti_pinmux_attach(device_t dev) { struct ti_pinmux_softc *sc = device_get_softc(dev); #if 0 if (ti_pinmux_sc) return (ENXIO); #endif sc->sc_dev = dev; if (bus_alloc_resources(dev, ti_pinmux_res_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_res[0]); sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); if (ti_pinmux_sc == NULL) ti_pinmux_sc = sc; fdt_pinctrl_register(dev, "pinctrl-single,pins"); fdt_pinctrl_configure_tree(dev); return (0); } static device_method_t ti_pinmux_methods[] = { DEVMETHOD(device_probe, ti_pinmux_probe), DEVMETHOD(device_attach, ti_pinmux_attach), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, ti_pinmux_configure_pins), { 0, 0 } }; static driver_t ti_pinmux_driver = { "ti_pinmux", ti_pinmux_methods, sizeof(struct ti_pinmux_softc), }; static devclass_t ti_pinmux_devclass; DRIVER_MODULE(ti_pinmux, simplebus, ti_pinmux_driver, ti_pinmux_devclass, 0, 0); +MODULE_VERSION(ti_pinmux, 1); +MODULE_DEPEND(ti_pinmux, ti_scm, 1, 1, 1); Index: head/sys/arm/ti/ti_prcm.c =================================================================== --- head/sys/arm/ti/ti_prcm.c (revision 363699) +++ head/sys/arm/ti/ti_prcm.c (revision 363700) @@ -1,357 +1,345 @@ /*- - * SPDX-License-Identifier: BSD-4-Clause + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2010 - * Ben Gray . + * Copyright (c) 2012 Damjan Marion * All rights reserved. * + * Copyright (c) 2020 Oskar Holmlund + * * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Ben Gray. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR + * 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 BEN GRAY 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. - */ - -/** - * Power, Reset and Clock Management Module + * 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. * - * This is a very simple driver wrapper around the PRCM set of registers in - * the OMAP3 chip. It allows you to turn on and off things like the functional - * and interface clocks to the various on-chip modules. - * + * $FreeBSD$ */ + +/* Based on sys/arm/ti/am335x/am335x_prcm.c */ + #include __FBSDID("$FreeBSD$"); #include #include +#include #include #include -#include -#include +#include #include -#include -#include - +#include +#include +#include #include -#include +#include #include #include #include +#include -/** - * ti_*_clk_devmap - Array of clock devices, should be defined one per SoC - * - * This array is typically defined in one of the targeted *_prcm_clk.c - * files and is specific to the given SoC platform. Each entry in the array - * corresponds to an individual clock device. - */ -extern struct ti_clock_dev ti_omap4_clk_devmap[]; -extern struct ti_clock_dev ti_am335x_clk_devmap[]; +#include -/** - * ti_prcm_clk_dev - returns a pointer to the clock device with given id - * @clk: the ID of the clock device to get - * - * Simply iterates through the clk_devmap global array and returns a pointer - * to the clock device if found. - * - * LOCKING: - * None - * - * RETURNS: - * The pointer to the clock device on success, on failure NULL is returned. - */ -static struct ti_clock_dev * -ti_prcm_clk_dev(clk_ident_t clk) +#include +#include +#include + +#include "clkdev_if.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +struct ti_prcm_softc { + struct simplebus_softc sc_simplebus; + device_t dev; + struct resource * mem_res; + bus_space_tag_t bst; + bus_space_handle_t bsh; + int attach_done; + struct mtx mtx; +}; + +static struct ti_prcm_softc *ti_prcm_sc = NULL; +static void omap4_prcm_reset(void); +static void am335x_prcm_reset(void); + +#define TI_AM3_PRCM 18 +#define TI_AM4_PRCM 17 +#define TI_OMAP2_PRCM 16 +#define TI_OMAP3_PRM 15 +#define TI_OMAP3_CM 14 +#define TI_OMAP4_CM1 13 +#define TI_OMAP4_PRM 12 +#define TI_OMAP4_CM2 11 +#define TI_OMAP4_SCRM 10 +#define TI_OMAP5_PRM 9 +#define TI_OMAP5_CM_CORE_AON 8 +#define TI_OMAP5_SCRM 7 +#define TI_OMAP5_CM_CORE 6 +#define TI_DRA7_PRM 5 +#define TI_DRA7_CM_CORE_AON 4 +#define TI_DRA7_CM_CORE 3 +#define TI_DM814_PRCM 2 +#define TI_DM816_PRCM 1 +#define TI_PRCM_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,am3-prcm", TI_AM3_PRCM }, + { "ti,am4-prcm", TI_AM4_PRCM }, + { "ti,omap2-prcm", TI_OMAP2_PRCM }, + { "ti,omap3-prm", TI_OMAP3_PRM }, + { "ti,omap3-cm", TI_OMAP3_CM }, + { "ti,omap4-cm1", TI_OMAP4_CM1 }, + { "ti,omap4-prm", TI_OMAP4_PRM }, + { "ti,omap4-cm2", TI_OMAP4_CM2 }, + { "ti,omap4-scrm", TI_OMAP4_SCRM }, + { "ti,omap5-prm", TI_OMAP5_PRM }, + { "ti,omap5-cm-core-aon", TI_OMAP5_CM_CORE_AON }, + { "ti,omap5-scrm", TI_OMAP5_SCRM }, + { "ti,omap5-cm-core", TI_OMAP5_CM_CORE }, + { "ti,dra7-prm", TI_DRA7_PRM }, + { "ti,dra7-cm-core-aon", TI_DRA7_CM_CORE_AON }, + { "ti,dra7-cm-core", TI_DRA7_CM_CORE }, + { "ti,dm814-prcm", TI_DM814_PRCM }, + { "ti,dm816-prcm", TI_DM816_PRCM }, + { NULL, TI_PRCM_END} +}; + +static int +ti_prcm_probe(device_t dev) { - struct ti_clock_dev *clk_dev; - - /* Find the clock within the devmap - it's a bit inefficent having a for - * loop for this, but this function should only called when a driver is - * being activated so IMHO not a big issue. + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) { + return (ENXIO); + } + + device_set_desc(dev, "TI Power and Clock Management"); + return(BUS_PROBE_DEFAULT); +} + +static int +ti_prcm_attach(device_t dev) +{ + struct ti_prcm_softc *sc; + phandle_t node, child; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + node = ofw_bus_get_node(sc->dev); + simplebus_init(sc->dev, node); + + if (simplebus_fill_ranges(node, &sc->sc_simplebus) < 0) { + device_printf(sc->dev, "could not get ranges\n"); + return (ENXIO); + } + if (sc->sc_simplebus.nranges == 0) { + device_printf(sc->dev, "nranges == 0\n"); + return (ENXIO); + } + + sc->mem_res = bus_alloc_resource(sc->dev, SYS_RES_MEMORY, &rid, + sc->sc_simplebus.ranges[0].host, + (sc->sc_simplebus.ranges[0].host + + sc->sc_simplebus.ranges[0].size - 1), + sc->sc_simplebus.ranges[0].size, + RF_ACTIVE | RF_SHAREABLE); + + if (sc->mem_res == NULL) { + return (ENXIO); + } + + sc->bst = rman_get_bustag(sc->mem_res); + sc->bsh = rman_get_bushandle(sc->mem_res); + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Fixme: for xxx_prcm_reset functions. + * Get rid of global variables? */ - clk_dev = NULL; + ti_prcm_sc = sc; + switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: - clk_dev = &(ti_omap4_clk_devmap[0]); + ti_cpu_reset = omap4_prcm_reset; break; #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: - clk_dev = &(ti_am335x_clk_devmap[0]); + ti_cpu_reset = am335x_prcm_reset; break; #endif } - if (clk_dev == NULL) - panic("No clock devmap found"); - while (clk_dev->id != INVALID_CLK_IDENT) { - if (clk_dev->id == clk) { - return (clk_dev); - } - clk_dev++; + + bus_generic_probe(sc->dev); + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + simplebus_add_device(dev, child, 0, NULL, -1, NULL); } - /* Sanity check we managed to find the clock */ - printf("ti_prcm: Failed to find clock device (%d)\n", clk); - return (NULL); + return (bus_generic_attach(sc->dev)); } -/** - * ti_prcm_clk_valid - enables a clock for a particular module - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. - * - * This function can enable either a functional or interface clock. - * - * The real work done to enable the clock is really done in the callback - * function associated with the clock, this function is simply a wrapper - * around that. - * - * LOCKING: - * Internally locks the driver context. - * - * RETURNS: - * Returns 0 on success or positive error code on failure. - */ int -ti_prcm_clk_valid(clk_ident_t clk) +ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val) { - int ret = 0; + struct ti_prcm_softc *sc; - if (ti_prcm_clk_dev(clk) == NULL) - ret = EINVAL; - - return (ret); + sc = device_get_softc(dev); + DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val); + bus_space_write_4(sc->bst, sc->bsh, addr, val); + return (0); } - - -/** - * ti_prcm_clk_enable - enables a clock for a particular module - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. - * - * This function can enable either a functional or interface clock. - * - * The real work done to enable the clock is really done in the callback - * function associated with the clock, this function is simply a wrapper - * around that. - * - * LOCKING: - * Internally locks the driver context. - * - * RETURNS: - * Returns 0 on success or positive error code on failure. - */ int -ti_prcm_clk_enable(clk_ident_t clk) +ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { - struct ti_clock_dev *clk_dev; - int ret; + struct ti_prcm_softc *sc; - /* Find the clock within the devmap - it's a bit inefficent having a for - * loop for this, but this function should only called when a driver is - * being activated so IMHO not a big issue. - */ - clk_dev = ti_prcm_clk_dev(clk); + sc = device_get_softc(dev); - /* Sanity check we managed to find the clock */ - if (clk_dev == NULL) - return (EINVAL); - - /* Activate the clock */ - if (clk_dev->clk_activate) - ret = clk_dev->clk_activate(clk_dev); - else - ret = EINVAL; - - return (ret); + *val = bus_space_read_4(sc->bst, sc->bsh, addr); + DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val); + return (0); } - -/** - * ti_prcm_clk_disable - disables a clock for a particular module - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. - * - * This function can enable either a functional or interface clock. - * - * The real work done to enable the clock is really done in the callback - * function associated with the clock, this function is simply a wrapper - * around that. - * - * LOCKING: - * Internally locks the driver context. - * - * RETURNS: - * Returns 0 on success or positive error code on failure. - */ int -ti_prcm_clk_disable(clk_ident_t clk) +ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) { - struct ti_clock_dev *clk_dev; - int ret; + struct ti_prcm_softc *sc; + uint32_t reg; - /* Find the clock within the devmap - it's a bit inefficent having a for - * loop for this, but this function should only called when a driver is - * being activated so IMHO not a big issue. - */ - clk_dev = ti_prcm_clk_dev(clk); + sc = device_get_softc(dev); - /* Sanity check we managed to find the clock */ - if (clk_dev == NULL) - return (EINVAL); + reg = bus_space_read_4(sc->bst, sc->bsh, addr); + reg &= ~clr; + reg |= set; + bus_space_write_4(sc->bst, sc->bsh, addr, reg); + DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set); - /* Activate the clock */ - if (clk_dev->clk_deactivate) - ret = clk_dev->clk_deactivate(clk_dev); - else - ret = EINVAL; - - return (ret); + return (0); } -/** - * ti_prcm_clk_set_source - sets the source - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. - * - * This function can enable either a functional or interface clock. - * - * The real work done to enable the clock is really done in the callback - * function associated with the clock, this function is simply a wrapper - * around that. - * - * LOCKING: - * Internally locks the driver context. - * - * RETURNS: - * Returns 0 on success or positive error code on failure. - */ -int -ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) +void +ti_prcm_device_lock(device_t dev) { - struct ti_clock_dev *clk_dev; - int ret; + struct ti_prcm_softc *sc; - /* Find the clock within the devmap - it's a bit inefficent having a for - * loop for this, but this function should only called when a driver is - * being activated so IMHO not a big issue. - */ - clk_dev = ti_prcm_clk_dev(clk); + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} - /* Sanity check we managed to find the clock */ - if (clk_dev == NULL) - return (EINVAL); +void +ti_prcm_device_unlock(device_t dev) +{ + struct ti_prcm_softc *sc; - /* Activate the clock */ - if (clk_dev->clk_set_source) - ret = clk_dev->clk_set_source(clk_dev, clksrc); - else - ret = EINVAL; - - return (ret); + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); } +static device_method_t ti_prcm_methods[] = { + DEVMETHOD(device_probe, ti_prcm_probe), + DEVMETHOD(device_attach, ti_prcm_attach), -/** - * ti_prcm_clk_get_source_freq - gets the source clock frequency - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * @freq: pointer to an integer that upon return will contain the src freq - * - * This function returns the frequency of the source clock. - * - * The real work done to enable the clock is really done in the callback - * function associated with the clock, this function is simply a wrapper - * around that. - * - * LOCKING: - * Internally locks the driver context. - * - * RETURNS: - * Returns 0 on success or positive error code on failure. - */ -int -ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) -{ - struct ti_clock_dev *clk_dev; - int ret; + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, ti_prcm_write_4), + DEVMETHOD(clkdev_read_4, ti_prcm_read_4), + DEVMETHOD(clkdev_modify_4, ti_prcm_modify_4), + DEVMETHOD(clkdev_device_lock, ti_prcm_device_lock), + DEVMETHOD(clkdev_device_unlock, ti_prcm_device_unlock), - /* Find the clock within the devmap - it's a bit inefficent having a for - * loop for this, but this function should only called when a driver is - * being activated so IMHO not a big issue. - */ - clk_dev = ti_prcm_clk_dev(clk); + DEVMETHOD_END +}; - /* Sanity check we managed to find the clock */ - if (clk_dev == NULL) - return (EINVAL); +DEFINE_CLASS_1(ti_prcm, ti_prcm_driver, ti_prcm_methods, + sizeof(struct ti_prcm_softc), simplebus_driver); - /* Get the source frequency of the clock */ - if (clk_dev->clk_get_source_freq) - ret = clk_dev->clk_get_source_freq(clk_dev, freq); - else - ret = EINVAL; +static devclass_t ti_prcm_devclass; - return (ret); +EARLY_DRIVER_MODULE(ti_prcm, ofwbus, ti_prcm_driver, + ti_prcm_devclass, 0, 0, BUS_PASS_BUS); +EARLY_DRIVER_MODULE(ti_prcm, simplebus, ti_prcm_driver, + ti_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_prcm, 1); +MODULE_DEPEND(ti_prcm, ti_scm, 1, 1, 1); + + +/* From sys/arm/ti/am335x/am335x_prcm.c + * Copyright (c) 2012 Damjan Marion + */ +#define PRM_DEVICE_OFFSET 0xF00 +#define AM335x_PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00) + +static void +am335x_prcm_reset(void) +{ + ti_prcm_write_4(ti_prcm_sc->dev, AM335x_PRM_RSTCTRL, (1<<1)); } -/** - * ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible - * @clk: identifier for the module to enable, see ti_prcm.h for a list - * of possible modules. - * @freq: requested freq +/* FIXME: Is this correct - or should the license part be ontop? */ + +/* From sys/arm/ti/omap4/omap4_prcm_clks.c */ +/*- + * SPDX-License-Identifier: BSD-3-Clause * - * LOCKING: - * Internally locks the driver context. + * Copyright (c) 2011 + * Ben Gray . + * All rights reserved. * - * RETURNS: - * Returns 0 on success or positive error code on failure. + * 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. + * 3. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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. */ -int -ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq) +#define PRM_RSTCTRL 0x1b00 +#define PRM_RSTCTRL_RESET 0x2 + +static void +omap4_prcm_reset(void) { - struct ti_clock_dev *clk_dev; - int ret; + uint32_t reg; - clk_dev = ti_prcm_clk_dev(clk); - - /* Sanity check we managed to find the clock */ - if (clk_dev == NULL) - return (EINVAL); - - /* Get the source frequency of the clock */ - if (clk_dev->clk_set_source_freq) - ret = clk_dev->clk_set_source_freq(clk_dev, freq); - else - ret = EINVAL; - - return (ret); + ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, ®); + reg = reg | PRM_RSTCTRL_RESET; + ti_prcm_write_4(ti_prcm_sc->dev, PRM_RSTCTRL, reg); + ti_prcm_read_4(ti_prcm_sc->dev, PRM_RSTCTRL, ®); } Index: head/sys/arm/ti/ti_prcm.h =================================================================== --- head/sys/arm/ti/ti_prcm.h (revision 363699) +++ head/sys/arm/ti/ti_prcm.h (revision 363700) @@ -1,209 +1,39 @@ /*- - * SPDX-License-Identifier: BSD-4-Clause + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2010 - * Ben Gray . - * All rights reserved. + * Copyright (c) 2020 Oskar Holmlund * * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Ben Gray. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR + * 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 BEN GRAY 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. + * 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. * * $FreeBSD$ */ +#ifndef __TI_PRCM_H__ +#define __TI_PRCM_H__ -/* - * Texas Instruments - OMAP3xxx series processors - * - * Reference: - * OMAP35x Applications Processor - * Technical Reference Manual - * (omap35xx_techref.pdf) - */ -#ifndef _TI_PRCM_H_ -#define _TI_PRCM_H_ +int ti_prcm_write_4(device_t dev, bus_addr_t addr, uint32_t val); +int ti_prcm_read_4(device_t dev, bus_addr_t addr, uint32_t *val); +int ti_prcm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set); +void ti_prcm_device_lock(device_t dev); +void ti_prcm_device_unlock(device_t dev); -typedef enum { - - INVALID_CLK_IDENT = 0, - - /* System clocks, typically you can only call ti_prcm_clk_get_source_freq() - * on these clocks as they are enabled by default. - */ - SYS_CLK = 1, - - /* The MPU (ARM) core clock */ - MPU_CLK = 20, - - /* MMC modules */ - MMC1_CLK = 100, - MMC2_CLK, - MMC3_CLK, - MMC4_CLK, - MMC5_CLK, - MMC6_CLK, - - /* I2C modules */ - I2C1_CLK = 200, - I2C2_CLK, - I2C3_CLK, - I2C4_CLK, - I2C5_CLK, - - /* USB module(s) */ - USBTLL_CLK = 300, - USBHSHOST_CLK, - USBFSHOST_CLK, - USBP1_PHY_CLK, - USBP2_PHY_CLK, - USBP1_UTMI_CLK, - USBP2_UTMI_CLK, - USBP1_HSIC_CLK, - USBP2_HSIC_CLK, - - /* UART modules */ - UART1_CLK = 400, - UART2_CLK, - UART3_CLK, - UART4_CLK, - UART5_CLK, - UART6_CLK, - UART7_CLK, - UART8_CLK, - UART9_CLK, - - /* General purpose timer modules */ - TIMER1_CLK = 500, - TIMER2_CLK, - TIMER3_CLK, - TIMER4_CLK, - TIMER5_CLK, - TIMER6_CLK, - TIMER7_CLK, - TIMER8_CLK, - TIMER9_CLK, - TIMER10_CLK, - TIMER11_CLK, - TIMER12_CLK, - - /* McBSP module(s) */ - MCBSP1_CLK = 600, - MCBSP2_CLK, - MCBSP3_CLK, - MCBSP4_CLK, - MCBSP5_CLK, - - /* General purpose I/O modules */ - GPIO1_CLK = 700, - GPIO2_CLK, - GPIO3_CLK, - GPIO4_CLK, - GPIO5_CLK, - GPIO6_CLK, - GPIO7_CLK, - - /* sDMA module */ - SDMA_CLK = 800, - - /* CPSW modules */ - CPSW_CLK = 1000, - - /* Mentor USB modules */ - MUSB0_CLK = 1100, - - /* EDMA module */ - EDMA_TPCC_CLK = 1200, - EDMA_TPTC0_CLK, - EDMA_TPTC1_CLK, - EDMA_TPTC2_CLK, - - /* LCD controller module */ - LCDC_CLK = 1300, - - /* PWM modules */ - PWMSS0_CLK = 1400, - PWMSS1_CLK, - PWMSS2_CLK, - - /* Mailbox modules */ - MAILBOX0_CLK = 1500, - - /* Spinlock modules */ - SPINLOCK0_CLK = 1600, - - PRUSS_CLK = 1700, - - TSC_ADC_CLK = 1800, - - /* RTC module */ - RTC_CLK = 1900, - - /* McSPI */ - SPI0_CLK = 2000, - SPI1_CLK, -} clk_ident_t; - -/* - * - */ -typedef enum { - SYSCLK_CLK, /* System clock */ - EXT_CLK, - - F32KHZ_CLK, /* 32KHz clock */ - F48MHZ_CLK, /* 48MHz clock */ - F64MHZ_CLK, /* 64MHz clock */ - F96MHZ_CLK, /* 96MHz clock */ - -} clk_src_t; - -struct ti_clock_dev { - /* The profile of the timer */ - clk_ident_t id; - - /* A bunch of callbacks associated with the clock device */ - int (*clk_activate)(struct ti_clock_dev *clkdev); - int (*clk_deactivate)(struct ti_clock_dev *clkdev); - int (*clk_set_source)(struct ti_clock_dev *clkdev, - clk_src_t clksrc); - int (*clk_accessible)(struct ti_clock_dev *clkdev); - int (*clk_set_source_freq)(struct ti_clock_dev *clkdev, - unsigned int freq); - int (*clk_get_source_freq)(struct ti_clock_dev *clkdev, - unsigned int *freq); -}; - -int ti_prcm_clk_valid(clk_ident_t clk); -int ti_prcm_clk_enable(clk_ident_t clk); -int ti_prcm_clk_disable(clk_ident_t clk); -int ti_prcm_clk_accessible(clk_ident_t clk); -int ti_prcm_clk_disable_autoidle(clk_ident_t clk); -int ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc); -int ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq); -int ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq); -void ti_prcm_reset(void); - -#endif /* _TI_PRCM_H_ */ +#endif Index: head/sys/arm/ti/ti_prm.c =================================================================== --- head/sys/arm/ti/ti_prm.c (nonexistent) +++ head/sys/arm/ti/ti_prm.c (revision 363700) @@ -0,0 +1,210 @@ +/*- + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +/* + * Power management - simple driver to handle reset and give access to + * memory space region for other drivers through prcm driver. + * Documentation/devicetree/binding/arm/omap/prm-inst.txt + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* relative to prcm address range */ +#define TI_PRM_PER_RSTCTRL 0xC00 + +struct ti_prm_softc { + device_t dev; + uint8_t type; + bool has_reset; +}; + +/* Device */ +#define TI_OMAP_PRM_INST 10 + +#define TI_AM3_PRM_INST 5 +#define TI_AM4_PRM_INST 4 +#define TI_OMAP4_PRM_INST 3 +#define TI_OMAP5_PRM_INST 2 +#define TI_DRA7_PRM_INST 1 +#define TI_END 0 + +static struct ofw_compat_data compat_data[] = { + { "ti,am3-prm-inst", TI_AM3_PRM_INST }, + { "ti,am4-prm-inst", TI_AM4_PRM_INST }, + { "ti,omap4-prm-inst", TI_OMAP4_PRM_INST }, + { "ti,omap5-prm-inst", TI_OMAP5_PRM_INST }, + { "ti,dra7-prm-inst", TI_DRA7_PRM_INST }, + { NULL, TI_END } +}; + +static struct ofw_compat_data required_data[] = { + { "ti,omap-prm-inst", TI_OMAP_PRM_INST }, + { NULL, TI_END } +}; + +/* device interface */ +static int +ti_prm_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, required_data)->ocd_data == 0) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI OMAP Power Management"); + return(BUS_PROBE_DEFAULT); +} + +static int +ti_prm_attach(device_t dev) +{ + struct ti_prm_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + node = ofw_bus_get_node(sc->dev); + + if (OF_hasprop(node, "#reset-cells")) { + sc->has_reset = true; + } else + sc->has_reset = false; + + /* Make device visible for other drivers */ + OF_device_register_xref(OF_xref_from_node(node), sc->dev); + + return (0); +} + +static int +ti_prm_detach(device_t dev) { + return (EBUSY); +} + +int +ti_prm_reset(device_t dev) +{ + struct ti_prm_softc *sc; + int err; + + sc = device_get_softc(dev); + if (sc->has_reset == false) + return 1; + + err = ti_prm_modify_4(dev, TI_PRM_PER_RSTCTRL, 0x2, 0x00); + return (err); +} + +int +ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct ti_prm_softc *sc; + device_t parent; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val); + ti_prcm_device_lock(parent); + ti_prcm_write_4(parent, addr, val); + ti_prcm_device_unlock(parent); + return (0); +} + +int +ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct ti_prm_softc *sc; + device_t parent; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + + ti_prcm_device_lock(parent); + ti_prcm_read_4(parent, addr, val); + ti_prcm_device_unlock(parent); + DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val); + return (0); +} + +int +ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct ti_prm_softc *sc; + device_t parent; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + + ti_prcm_device_lock(parent); + ti_prcm_modify_4(parent, addr, clr, set); + ti_prcm_device_unlock(parent); + DPRINTF(sc->dev, "offset=%lx (clr %x set %x)\n", addr, clr, set); + + return (0); +} + +static device_method_t ti_prm_methods[] = { + DEVMETHOD(device_probe, ti_prm_probe), + DEVMETHOD(device_attach, ti_prm_attach), + DEVMETHOD(device_detach, ti_prm_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ti_prm, ti_prm_driver, ti_prm_methods, + sizeof(struct ti_prm_softc), simplebus_driver); + +static devclass_t ti_prm_devclass; + +EARLY_DRIVER_MODULE(ti_prm, simplebus, ti_prm_driver, + ti_prm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_prm, 1); +MODULE_DEPEND(ti_prm, ti_sysc, 1, 1, 1); Property changes on: head/sys/arm/ti/ti_prm.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_prm.h =================================================================== --- head/sys/arm/ti/ti_prm.h (nonexistent) +++ head/sys/arm/ti/ti_prm.h (revision 363700) @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +#ifndef __TI_PRM__ +#define __TI_PRM__ + +int ti_prm_reset(device_t dev); + +int ti_prm_write_4(device_t dev, bus_addr_t addr, uint32_t val); +int ti_prm_read_4(device_t dev, bus_addr_t addr, uint32_t *val); +int ti_prm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set); + +#endif /* __TI_PRM__ */ Property changes on: head/sys/arm/ti/ti_prm.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_pruss.c =================================================================== --- head/sys/arm/ti/ti_pruss.c (revision 363699) +++ head/sys/arm/ti/ti_pruss.c (revision 363700) @@ -1,772 +1,854 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Rui Paulo * Copyright (c) 2017 Manuel Stuehn * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include + +#include #include +#include #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static d_open_t ti_pruss_irq_open; static d_read_t ti_pruss_irq_read; static d_poll_t ti_pruss_irq_poll; static device_probe_t ti_pruss_probe; static device_attach_t ti_pruss_attach; static device_detach_t ti_pruss_detach; static void ti_pruss_intr(void *); static d_open_t ti_pruss_open; static d_mmap_t ti_pruss_mmap; static void ti_pruss_irq_kqread_detach(struct knote *); static int ti_pruss_irq_kqevent(struct knote *, long); static d_kqfilter_t ti_pruss_irq_kqfilter; static void ti_pruss_privdtor(void *data); #define TI_PRUSS_PRU_IRQS 2 #define TI_PRUSS_HOST_IRQS 8 #define TI_PRUSS_IRQS (TI_PRUSS_HOST_IRQS+TI_PRUSS_PRU_IRQS) #define TI_PRUSS_EVENTS 64 #define NOT_SET_STR "NONE" #define TI_TS_ARRAY 16 struct ctl { size_t cnt; size_t idx; }; struct ts_ring_buf { struct ctl ctl; uint64_t ts[TI_TS_ARRAY]; }; struct ti_pruss_irqsc { struct mtx sc_mtx; struct cdev *sc_pdev; struct selinfo sc_selinfo; int8_t channel; int8_t last; int8_t event; bool enable; struct ts_ring_buf tstamps; }; static struct cdevsw ti_pruss_cdevirq = { .d_version = D_VERSION, .d_name = "ti_pruss_irq", .d_open = ti_pruss_irq_open, .d_read = ti_pruss_irq_read, .d_poll = ti_pruss_irq_poll, .d_kqfilter = ti_pruss_irq_kqfilter, }; struct ti_pruss_softc { struct mtx sc_mtx; struct resource *sc_mem_res; struct resource *sc_irq_res[TI_PRUSS_HOST_IRQS]; void *sc_intr[TI_PRUSS_HOST_IRQS]; struct ti_pruss_irqsc sc_irq_devs[TI_PRUSS_IRQS]; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; struct cdev *sc_pdev; struct selinfo sc_selinfo; bool sc_glob_irqen; }; static struct cdevsw ti_pruss_cdevsw = { .d_version = D_VERSION, .d_name = "ti_pruss", .d_open = ti_pruss_open, .d_mmap = ti_pruss_mmap, }; static device_method_t ti_pruss_methods[] = { DEVMETHOD(device_probe, ti_pruss_probe), DEVMETHOD(device_attach, ti_pruss_attach), DEVMETHOD(device_detach, ti_pruss_detach), DEVMETHOD_END }; static driver_t ti_pruss_driver = { "ti_pruss", ti_pruss_methods, sizeof(struct ti_pruss_softc) }; static devclass_t ti_pruss_devclass; DRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0); -MODULE_DEPEND(ti_pruss, ti_prcm, 1, 1, 1); +MODULE_DEPEND(ti_pruss, ti_sysc, 1, 1, 1); +MODULE_DEPEND(ti_pruss, ti_prm, 1, 1, 1); static struct resource_spec ti_pruss_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { -1, 0, 0 } }; CTASSERT(TI_PRUSS_HOST_IRQS == nitems(ti_pruss_irq_spec) - 1); static int ti_pruss_irq_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct ctl* irqs; struct ti_pruss_irqsc *sc; sc = dev->si_drv1; irqs = malloc(sizeof(struct ctl), M_DEVBUF, M_WAITOK); if (!irqs) return (ENOMEM); irqs->cnt = sc->tstamps.ctl.cnt; irqs->idx = sc->tstamps.ctl.idx; return devfs_set_cdevpriv(irqs, ti_pruss_privdtor); } static void ti_pruss_privdtor(void *data) { free(data, M_DEVBUF); } static int ti_pruss_irq_poll(struct cdev *dev, int events, struct thread *td) { struct ctl* irqs; struct ti_pruss_irqsc *sc; sc = dev->si_drv1; devfs_get_cdevpriv((void**)&irqs); if (events & (POLLIN | POLLRDNORM)) { if (sc->tstamps.ctl.cnt != irqs->cnt) return events & (POLLIN | POLLRDNORM); else selrecord(td, &sc->sc_selinfo); } return 0; } static int ti_pruss_irq_read(struct cdev *cdev, struct uio *uio, int ioflag) { const size_t ts_len = sizeof(uint64_t); struct ti_pruss_irqsc* irq; struct ctl* priv; int error = 0; size_t idx; ssize_t level; irq = cdev->si_drv1; if (uio->uio_resid < ts_len) return (EINVAL); error = devfs_get_cdevpriv((void**)&priv); if (error) return (error); mtx_lock(&irq->sc_mtx); if (irq->tstamps.ctl.cnt - priv->cnt > TI_TS_ARRAY) { priv->cnt = irq->tstamps.ctl.cnt; priv->idx = irq->tstamps.ctl.idx; mtx_unlock(&irq->sc_mtx); return (ENXIO); } do { idx = priv->idx; level = irq->tstamps.ctl.idx - idx; if (level < 0) level += TI_TS_ARRAY; if (level == 0) { if (ioflag & O_NONBLOCK) { mtx_unlock(&irq->sc_mtx); return (EWOULDBLOCK); } error = msleep(irq, &irq->sc_mtx, PCATCH | PDROP, "pruirq", 0); if (error) return error; mtx_lock(&irq->sc_mtx); } }while(level == 0); mtx_unlock(&irq->sc_mtx); error = uiomove(&irq->tstamps.ts[idx], ts_len, uio); if (++idx == TI_TS_ARRAY) idx = 0; priv->idx = idx; atomic_add_32(&priv->cnt, 1); return (error); } static struct ti_pruss_irq_arg { int irq; struct ti_pruss_softc *sc; } ti_pruss_irq_args[TI_PRUSS_IRQS]; static __inline uint32_t ti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static __inline void ti_pruss_interrupts_clear(struct ti_pruss_softc *sc) { /* disable global interrupt */ ti_pruss_reg_write(sc, PRUSS_INTC_GER, 0 ); /* clear all events */ ti_pruss_reg_write(sc, PRUSS_INTC_SECR0, 0xFFFFFFFF); ti_pruss_reg_write(sc, PRUSS_INTC_SECR1, 0xFFFFFFFF); /* disable all host interrupts */ ti_pruss_reg_write(sc, PRUSS_INTC_HIER, 0); } static __inline int ti_pruss_interrupts_enable(struct ti_pruss_softc *sc, int8_t irq, bool enable) { if (enable && ((sc->sc_irq_devs[irq].channel == -1) || (sc->sc_irq_devs[irq].event== -1))) { device_printf( sc->sc_pdev->si_drv1, "Interrupt chain not fully configured, not possible to enable\n" ); return (EINVAL); } sc->sc_irq_devs[irq].enable = enable; if (sc->sc_irq_devs[irq].sc_pdev) { destroy_dev(sc->sc_irq_devs[irq].sc_pdev); sc->sc_irq_devs[irq].sc_pdev = NULL; } if (enable) { sc->sc_irq_devs[irq].sc_pdev = make_dev(&ti_pruss_cdevirq, 0, UID_ROOT, GID_WHEEL, 0600, "pruss%d.irq%d", device_get_unit(sc->sc_pdev->si_drv1), irq); sc->sc_irq_devs[irq].sc_pdev->si_drv1 = &sc->sc_irq_devs[irq]; sc->sc_irq_devs[irq].tstamps.ctl.idx = 0; } uint32_t reg = enable ? PRUSS_INTC_HIEISR : PRUSS_INTC_HIDISR; ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].channel); reg = enable ? PRUSS_INTC_EISR : PRUSS_INTC_EICR; ti_pruss_reg_write(sc, reg, sc->sc_irq_devs[irq].event ); return (0); } static __inline void ti_pruss_map_write(struct ti_pruss_softc *sc, uint32_t basereg, uint8_t index, uint8_t content) { const size_t regadr = basereg + index & ~0x03; const size_t bitpos = (index & 0x03) * 8; uint32_t rmw = ti_pruss_reg_read(sc, regadr); rmw = (rmw & ~( 0xF << bitpos)) | ( (content & 0xF) << bitpos); ti_pruss_reg_write(sc, regadr, rmw); } static int ti_pruss_event_map( SYSCTL_HANDLER_ARGS ) { struct ti_pruss_softc *sc; const int8_t irq = arg2; int err; char event[sizeof(NOT_SET_STR)]; sc = arg1; if(sc->sc_irq_devs[irq].event == -1) bcopy(NOT_SET_STR, event, sizeof(event)); else snprintf(event, sizeof(event), "%d", sc->sc_irq_devs[irq].event); err = sysctl_handle_string(oidp, event, sizeof(event), req); if(err != 0) return (err); if (req->newptr) { // write event if (strcmp(NOT_SET_STR, event) == 0) { ti_pruss_interrupts_enable(sc, irq, false); sc->sc_irq_devs[irq].event = -1; } else { if (sc->sc_irq_devs[irq].channel == -1) { device_printf( sc->sc_pdev->si_drv1, "corresponding channel not configured\n"); return (ENXIO); } const int8_t channelnr = sc->sc_irq_devs[irq].channel; const int8_t eventnr = strtol( event, NULL, 10 ); // TODO: check if strol is valid if (eventnr > TI_PRUSS_EVENTS || eventnr < 0) { device_printf( sc->sc_pdev->si_drv1, "Event number %d not valid (0 - %d)", channelnr, TI_PRUSS_EVENTS -1); return (EINVAL); } sc->sc_irq_devs[irq].channel = channelnr; sc->sc_irq_devs[irq].event = eventnr; // event[nr] <= channel ti_pruss_map_write(sc, PRUSS_INTC_CMR_BASE, eventnr, channelnr); } } return (err); } static int ti_pruss_channel_map(SYSCTL_HANDLER_ARGS) { struct ti_pruss_softc *sc; int err; char channel[sizeof(NOT_SET_STR)]; const int8_t irq = arg2; sc = arg1; if (sc->sc_irq_devs[irq].channel == -1) bcopy(NOT_SET_STR, channel, sizeof(channel)); else snprintf(channel, sizeof(channel), "%d", sc->sc_irq_devs[irq].channel); err = sysctl_handle_string(oidp, channel, sizeof(channel), req); if (err != 0) return (err); if (req->newptr) { // write event if (strcmp(NOT_SET_STR, channel) == 0) { ti_pruss_interrupts_enable(sc, irq, false); ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, sc->sc_irq_devs[irq].channel); sc->sc_irq_devs[irq].channel = -1; } else { const int8_t channelnr = strtol(channel, NULL, 10); // TODO: check if strol is valid if (channelnr > TI_PRUSS_IRQS || channelnr < 0) { device_printf(sc->sc_pdev->si_drv1, "Channel number %d not valid (0 - %d)", channelnr, TI_PRUSS_IRQS-1); return (EINVAL); } sc->sc_irq_devs[irq].channel = channelnr; sc->sc_irq_devs[irq].last = -1; // channel[nr] <= irqnr ti_pruss_map_write(sc, PRUSS_INTC_HMR_BASE, irq, channelnr); } } return (err); } static int ti_pruss_interrupt_enable(SYSCTL_HANDLER_ARGS) { struct ti_pruss_softc *sc; int err; bool irqenable; const int8_t irq = arg2; sc = arg1; irqenable = sc->sc_irq_devs[arg2].enable; err = sysctl_handle_bool(oidp, &irqenable, arg2, req); if (err != 0) return (err); if (req->newptr) // write enable return ti_pruss_interrupts_enable(sc, irq, irqenable); return (err); } static int ti_pruss_global_interrupt_enable(SYSCTL_HANDLER_ARGS) { struct ti_pruss_softc *sc; int err; bool glob_irqen; sc = arg1; glob_irqen = sc->sc_glob_irqen; err = sysctl_handle_bool(oidp, &glob_irqen, arg2, req); if (err != 0) return (err); if (req->newptr) { sc->sc_glob_irqen = glob_irqen; ti_pruss_reg_write(sc, PRUSS_INTC_GER, glob_irqen); } return (err); } static int ti_pruss_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,pruss-v1") || ofw_bus_is_compatible(dev, "ti,pruss-v2")) { device_set_desc(dev, "TI Programmable Realtime Unit Subsystem"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_pruss_attach(device_t dev) { struct ti_pruss_softc *sc; - int rid, i; + int rid, i, err, ncells; + uint32_t reg; + phandle_t node; + clk_t l3_gclk, pruss_ocp_gclk; + phandle_t ti_prm_ref, *cells; + device_t ti_prm_dev; - if (ti_prcm_clk_enable(PRUSS_CLK) != 0) { - device_printf(dev, "could not enable PRUSS clock\n"); + rid = 0; + sc = device_get_softc(dev); + node = ofw_bus_get_node(device_get_parent(dev)); + if (node <= 0) { + device_printf(dev, "Cant get ofw node\n"); return (ENXIO); } - sc = device_get_softc(dev); - rid = 0; + + /* + * Follow activate pattern from sys/arm/ti/am335x/am335x_prcm.c + * by Damjan Marion + */ + + /* Set MODULEMODE to ENABLE(2) */ + /* Wait for MODULEMODE to become ENABLE(2) */ + if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) { + device_printf(dev, "Could not enable PRUSS clock\n"); + return (ENXIO); + } + + /* Set CLKTRCTRL to SW_WKUP(2) */ + /* Wait for the 200 MHz OCP clock to become active */ + /* Wait for the 200 MHz IEP clock to become active */ + /* Wait for the 192 MHz UART clock to become active */ + /* + * At the moment there is no reference to CM_PER_PRU_ICSS_CLKSTCTRL@140 + * in the devicetree. The register reset state are SW_WKUP(2) as default + * so at the moment ignore setting this register. + */ + + /* Select L3F as OCP clock */ + /* Get the clock and set the parent */ + err = clk_get_by_name(dev, "l3_gclk", &l3_gclk); + if (err) { + device_printf(dev, "Cant get l3_gclk err %d\n", err); + return (ENXIO); + } + + err = clk_get_by_name(dev, "pruss_ocp_gclk@530", &pruss_ocp_gclk); + if (err) { + device_printf(dev, "Cant get pruss_ocp_gclk@530 err %d\n", err); + return (ENXIO); + } + + err = clk_set_parent_by_clk(pruss_ocp_gclk, l3_gclk); + if (err) { + device_printf(dev, + "Cant set pruss_ocp_gclk parent to l3_gclk err %d\n", err); + return (ENXIO); + } + + /* Clear the RESET bit */ + /* Find the ti_prm */ + /* #reset-cells should not been used in this way but... */ + err = ofw_bus_parse_xref_list_alloc(node, "resets", "#reset-cells", 0, + &ti_prm_ref, &ncells, &cells); + OF_prop_free(cells); + if (err) { + device_printf(dev, + "Cant fetch \"resets\" reference %x\n", err); + return (ENXIO); + } + + ti_prm_dev = OF_device_from_xref(ti_prm_ref); + if (ti_prm_dev == NULL) { + device_printf(dev, "Cant get device from \"resets\"\n"); + return (ENXIO); + } + + err = ti_prm_reset(ti_prm_dev); + if (err) { + device_printf(dev, "ti_prm_reset failed %d\n", err); + return (ENXIO); + } + /* End of clock activation */ + mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF); sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } struct sysctl_ctx_list *clist = device_get_sysctl_ctx(dev); if (!clist) return (EINVAL); struct sysctl_oid *poid; poid = device_get_sysctl_tree( dev ); if (!poid) return (EINVAL); sc->sc_glob_irqen = false; struct sysctl_oid *irq_root = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(poid), OID_AUTO, "irq", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "PRUSS Host Interrupts"); SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(poid), OID_AUTO, "global_interrupt_enable", CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT, sc, 0, ti_pruss_global_interrupt_enable, "CU", "Global interrupt enable"); sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) { device_printf(dev, "could not allocate interrupt resource\n"); ti_pruss_detach(dev); return (ENXIO); } ti_pruss_interrupts_clear(sc); for (i = 0; i < TI_PRUSS_IRQS; i++) { char name[8]; snprintf(name, sizeof(name), "%d", i); struct sysctl_oid *irq_nodes = SYSCTL_ADD_NODE(clist, SYSCTL_CHILDREN(irq_root), OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "PRUSS Interrupts"); SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO, "channel", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, sc, i, ti_pruss_channel_map, "A", "Channel attached to this irq"); SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO, "event", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, sc, i, ti_pruss_event_map, "A", "Event attached to this irq"); SYSCTL_ADD_PROC(clist, SYSCTL_CHILDREN(irq_nodes), OID_AUTO, "enable", CTLFLAG_RW | CTLTYPE_U8 | CTLFLAG_NEEDGIANT, sc, i, ti_pruss_interrupt_enable, "CU", "Enable/Disable interrupt"); sc->sc_irq_devs[i].event = -1; sc->sc_irq_devs[i].channel = -1; sc->sc_irq_devs[i].tstamps.ctl.idx = 0; if (i < TI_PRUSS_HOST_IRQS) { ti_pruss_irq_args[i].irq = i; ti_pruss_irq_args[i].sc = sc; if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_pruss_intr, &ti_pruss_irq_args[i], &sc->sc_intr[i]) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_pruss_detach(dev); return (ENXIO); } mtx_init(&sc->sc_irq_devs[i].sc_mtx, "TI PRUSS IRQ", NULL, MTX_DEF); knlist_init_mtx(&sc->sc_irq_devs[i].sc_selinfo.si_note, &sc->sc_irq_devs[i].sc_mtx); } } + + reg = ti_pruss_reg_read(sc, + ti_sysc_get_sysc_address_offset_host(device_get_parent(dev))); if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV) device_printf(dev, "AM33xx PRU-ICSS\n"); sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "pruss%d", device_get_unit(dev)); sc->sc_pdev->si_drv1 = dev; /* Acc. to datasheet always write 1 to polarity registers */ ti_pruss_reg_write(sc, PRUSS_INTC_SIPR0, 0xFFFFFFFF); ti_pruss_reg_write(sc, PRUSS_INTC_SIPR1, 0xFFFFFFFF); /* Acc. to datasheet always write 0 to event type registers */ ti_pruss_reg_write(sc, PRUSS_INTC_SITR0, 0); ti_pruss_reg_write(sc, PRUSS_INTC_SITR1, 0); return (0); } static int ti_pruss_detach(device_t dev) { struct ti_pruss_softc *sc = device_get_softc(dev); ti_pruss_interrupts_clear(sc); for (int i = 0; i < TI_PRUSS_HOST_IRQS; i++) { ti_pruss_interrupts_enable( sc, i, false ); if (sc->sc_intr[i]) bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]); if (sc->sc_irq_res[i]) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res[i]), sc->sc_irq_res[i]); knlist_clear(&sc->sc_irq_devs[i].sc_selinfo.si_note, 0); mtx_lock(&sc->sc_irq_devs[i].sc_mtx); if (!knlist_empty(&sc->sc_irq_devs[i].sc_selinfo.si_note)) printf("IRQ %d KQueue not empty!\n", i ); mtx_unlock(&sc->sc_irq_devs[i].sc_mtx); knlist_destroy(&sc->sc_irq_devs[i].sc_selinfo.si_note); mtx_destroy(&sc->sc_irq_devs[i].sc_mtx); } mtx_destroy(&sc->sc_mtx); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); if (sc->sc_pdev) destroy_dev(sc->sc_pdev); return (0); } static void ti_pruss_intr(void *arg) { int val; struct ti_pruss_irq_arg *iap = arg; struct ti_pruss_softc *sc = iap->sc; /* * Interrupts pr1_host_intr[0:7] are mapped to * Host-2 to Host-9 of PRU-ICSS IRQ-controller. */ const int pru_int = iap->irq + TI_PRUSS_PRU_IRQS; const int pru_int_mask = (1 << pru_int); const int pru_channel = sc->sc_irq_devs[pru_int].channel; const int pru_event = sc->sc_irq_devs[pru_channel].event; val = ti_pruss_reg_read(sc, PRUSS_INTC_HIER); if (!(val & pru_int_mask)) return; ti_pruss_reg_write(sc, PRUSS_INTC_HIDISR, pru_int); ti_pruss_reg_write(sc, PRUSS_INTC_SICR, pru_event); ti_pruss_reg_write(sc, PRUSS_INTC_HIEISR, pru_int); struct ti_pruss_irqsc* irq = &sc->sc_irq_devs[pru_channel]; size_t wr = irq->tstamps.ctl.idx; struct timespec ts; nanouptime(&ts); irq->tstamps.ts[wr] = ts.tv_sec * 1000000000 + ts.tv_nsec; if (++wr == TI_TS_ARRAY) wr = 0; atomic_add_32(&irq->tstamps.ctl.cnt, 1); irq->tstamps.ctl.idx = wr; KNOTE_UNLOCKED(&irq->sc_selinfo.si_note, pru_int); wakeup(irq); selwakeup(&irq->sc_selinfo); } static int ti_pruss_open(struct cdev *cdev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused) { return (0); } static int ti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { device_t dev = cdev->si_drv1; struct ti_pruss_softc *sc = device_get_softc(dev); if (offset >= rman_get_size(sc->sc_mem_res)) return (ENOSPC); *paddr = rman_get_start(sc->sc_mem_res) + offset; *memattr = VM_MEMATTR_UNCACHEABLE; return (0); } static struct filterops ti_pruss_kq_read = { .f_isfd = 1, .f_detach = ti_pruss_irq_kqread_detach, .f_event = ti_pruss_irq_kqevent, }; static void ti_pruss_irq_kqread_detach(struct knote *kn) { struct ti_pruss_irqsc *sc = kn->kn_hook; knlist_remove(&sc->sc_selinfo.si_note, kn, 0); } static int ti_pruss_irq_kqevent(struct knote *kn, long hint) { struct ti_pruss_irqsc* irq_sc; int notify; irq_sc = kn->kn_hook; if (hint > 0) kn->kn_data = hint - 2; if (hint > 0 || irq_sc->last > 0) notify = 1; else notify = 0; irq_sc->last = hint; return (notify); } static int ti_pruss_irq_kqfilter(struct cdev *cdev, struct knote *kn) { struct ti_pruss_irqsc *sc = cdev->si_drv1; switch (kn->kn_filter) { case EVFILT_READ: kn->kn_hook = sc; kn->kn_fop = &ti_pruss_kq_read; knlist_add(&sc->sc_selinfo.si_note, kn, 0); break; default: return (EINVAL); } return (0); } Index: head/sys/arm/ti/ti_scm.c =================================================================== --- head/sys/arm/ti/ti_scm.c (revision 363699) +++ head/sys/arm/ti/ti_scm.c (revision 363700) @@ -1,181 +1,162 @@ /*- - * SPDX-License-Identifier: BSD-4-Clause + * Copyright (c) 2019 Emmanuel Vadot * - * Copyright (c) 2010 - * Ben Gray . - * All rights reserved. + * Copyright (c) 2020 Oskar Holmlund * * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Ben Gray. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. * - * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR + * 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 BEN GRAY 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. - */ - -/** - * SCM - System Control Module + * 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. * - * Hopefully in the end this module will contain a bunch of utility functions - * for configuring and querying the general system control registers, but for - * now it only does pin(pad) multiplexing. - * - * This is different from the GPIO module in that it is used to configure the - * pins between modules not just GPIO input/output. - * - * This file contains the generic top level driver, however it relies on chip - * specific settings and therefore expects an array of ti_scm_padconf structs - * call ti_padconf_devmap to be located somewhere in the kernel. - * + * $FreeBSD$ */ + #include __FBSDID("$FreeBSD$"); + +/* Based on sys/arm/ti/ti_sysc.c */ + #include #include +#include +#include #include #include -#include -#include #include -#include -#include - +#include #include -#include +#include +#include +#include +#include -#include +#include + #include #include -#include -#include "ti_scm.h" -#include "ti_cpuid.h" +#define TI_AM3_SCM 14 +#define TI_AM4_SCM 13 +#define TI_DM814_SCRM 12 +#define TI_DM816_SCRM 11 +#define TI_OMAP2_SCM 10 +#define TI_OMAP3_SCM 9 +#define TI_OMAP4_SCM_CORE 8 +#define TI_OMAP4_SCM_PADCONF_CORE 7 +#define TI_OMAP4_SCM_WKUP 6 +#define TI_OMAP4_SCM_PADCONF_WKUP 5 +#define TI_OMAP5_SCM_CORE 4 +#define TI_OMAP5_SCM_PADCONF_CORE 3 +#define TI_OMAP5_SCM_WKUP_PAD_CONF 2 +#define TI_DRA7_SCM_CORE 1 +#define TI_SCM_END 0 -static struct resource_spec ti_scm_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ - { -1, 0 } +static struct ofw_compat_data compat_data[] = { + { "ti,am3-scm", TI_AM3_SCM }, + { "ti,am4-scm", TI_AM4_SCM }, + { "ti,dm814-scrm", TI_DM814_SCRM }, + { "ti,dm816-scrm", TI_DM816_SCRM }, + { "ti,omap2-scm", TI_OMAP2_SCM }, + { "ti,omap3-scm", TI_OMAP3_SCM }, + { "ti,omap4-scm-core", TI_OMAP4_SCM_CORE }, + { "ti,omap4-scm-padconf-core", TI_OMAP4_SCM_PADCONF_CORE }, + { "ti,omap4-scm-wkup", TI_OMAP4_SCM_WKUP }, + { "ti,omap4-scm-padconf-wkup", TI_OMAP4_SCM_PADCONF_WKUP }, + { "ti,omap5-scm-core", TI_OMAP5_SCM_CORE }, + { "ti,omap5-scm-padconf-core", TI_OMAP5_SCM_PADCONF_CORE }, + { "ti,omap5-scm-wkup-pad-conf", TI_OMAP5_SCM_WKUP_PAD_CONF }, + { "ti,dra7-scm-core", TI_DRA7_SCM_CORE }, + { NULL, TI_SCM_END } }; -static struct ti_scm_softc *ti_scm_sc; +struct ti_scm_softc { + struct simplebus_softc sc; + device_t dev; +}; -#define ti_scm_read_4(sc, reg) \ - bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) -#define ti_scm_write_4(sc, reg, val) \ - bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) +static int ti_scm_probe(device_t dev); +static int ti_scm_attach(device_t dev); +static int ti_scm_detach(device_t dev); -/* - * Device part of OMAP SCM driver - */ static int ti_scm_probe(device_t dev) { - - if (!ti_soc_is_supported()) - return (ENXIO); - if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "syscon")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); - if (ti_scm_sc) { - return (EEXIST); - } + device_set_desc(dev, "TI OMAP Control Module"); - device_set_desc(dev, "TI Control Module"); return (BUS_PROBE_DEFAULT); } -/** - * ti_scm_attach - attaches the timer to the simplebus - * @dev: new device - * - * Reserves memory and interrupt resources, stores the softc structure - * globally and registers both the timecount and eventtimer objects. - * - * RETURNS - * Zero on success or ENXIO if an error occuried. - */ static int ti_scm_attach(device_t dev) { - struct ti_scm_softc *sc = device_get_softc(dev); + struct ti_scm_softc *sc; + device_t cdev; + phandle_t node, child; - sc->sc_dev = dev; + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); - if (bus_alloc_resources(dev, ti_scm_res_spec, sc->sc_res)) { - device_printf(dev, "could not allocate resources\n"); + simplebus_init(dev, node); + if (simplebus_fill_ranges(node, &sc->sc) < 0) { + device_printf(dev, "could not get ranges\n"); return (ENXIO); } - /* Global timer interface */ - sc->sc_bst = rman_get_bustag(sc->sc_res[0]); - sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } - ti_scm_sc = sc; - - /* Attach platform extensions, if any. */ - bus_generic_probe(dev); - return (bus_generic_attach(dev)); } -int -ti_scm_reg_read_4(uint32_t reg, uint32_t *val) +static int +ti_scm_detach(device_t dev) { - if (!ti_scm_sc) - return (ENXIO); - - *val = ti_scm_read_4(ti_scm_sc, reg); - return (0); + return (EBUSY); } -int -ti_scm_reg_write_4(uint32_t reg, uint32_t val) -{ - if (!ti_scm_sc) - return (ENXIO); - - ti_scm_write_4(ti_scm_sc, reg, val); - return (0); -} - - static device_method_t ti_scm_methods[] = { + /* Device interface */ DEVMETHOD(device_probe, ti_scm_probe), DEVMETHOD(device_attach, ti_scm_attach), + DEVMETHOD(device_detach, ti_scm_detach), - { 0, 0 } + DEVMETHOD_END }; -static driver_t ti_scm_driver = { - "ti_scm", - ti_scm_methods, - sizeof(struct ti_scm_softc), -}; +DEFINE_CLASS_1(ti_scm, ti_scm_driver, ti_scm_methods, + sizeof(struct ti_scm_softc), simplebus_driver); static devclass_t ti_scm_devclass; -EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, ti_scm_devclass, 0, 0, - BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, + ti_scm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); +MODULE_VERSION(ti_scm, 1); +MODULE_DEPEND(ti_scm, ti_sysc, 1, 1, 1); + Index: head/sys/arm/ti/ti_scm_syscon.c =================================================================== --- head/sys/arm/ti/ti_scm_syscon.c (nonexistent) +++ head/sys/arm/ti/ti_scm_syscon.c (revision 363700) @@ -0,0 +1,294 @@ +/*- + * Copyright (c) 2019 Emmanuel Vadot + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +/* Based on sys/arm/ti/ti_sysc.c */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "syscon_if.h" +#include +#include "clkdev_if.h" + +#if 0 +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +MALLOC_DECLARE(M_SYSCON); + +struct ti_scm_syscon_softc { + struct simplebus_softc sc_simplebus; + device_t dev; + struct syscon * syscon; + struct resource * res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct mtx mtx; +}; + +static struct resource_spec ti_scm_syscon_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +/* Device */ +static struct ofw_compat_data compat_data[] = { + { "syscon", 1 }, + { NULL, 0 } +}; + +/* --- dev/extres/syscon syscon_method_t interface --- */ +static int +ti_scm_syscon_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val) +{ + struct ti_scm_syscon_softc *sc; + + sc = device_get_softc(syscon->pdev); + DPRINTF(sc->dev, "offset=%lx write %x\n", offset, val); + mtx_lock(&sc->mtx); + bus_space_write_4(sc->bst, sc->bsh, offset, val); + mtx_unlock(&sc->mtx); + return (0); +} + +static uint32_t +ti_scm_syscon_read_4(struct syscon *syscon, bus_size_t offset) +{ + struct ti_scm_syscon_softc *sc; + uint32_t val; + + sc = device_get_softc(syscon->pdev); + + mtx_lock(&sc->mtx); + val = bus_space_read_4(sc->bst, sc->bsh, offset); + mtx_unlock(&sc->mtx); + DPRINTF(sc->dev, "offset=%lx Read %x\n", offset, val); + return (val); +} +static int +ti_scm_syscon_modify_4(struct syscon *syscon, bus_size_t offset, uint32_t clr, uint32_t set) +{ + struct ti_scm_syscon_softc *sc; + uint32_t reg; + + sc = device_get_softc(syscon->pdev); + + mtx_lock(&sc->mtx); + reg = bus_space_read_4(sc->bst, sc->bsh, offset); + reg &= ~clr; + reg |= set; + bus_space_write_4(sc->bst, sc->bsh, offset, reg); + mtx_unlock(&sc->mtx); + DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", offset, reg, clr, set); + + return (0); +} + +static syscon_method_t ti_scm_syscon_reg_methods[] = { + SYSCONMETHOD(syscon_read_4, ti_scm_syscon_read_4), + SYSCONMETHOD(syscon_write_4, ti_scm_syscon_write_4), + SYSCONMETHOD(syscon_modify_4, ti_scm_syscon_modify_4), + + SYSCONMETHOD_END +}; + +DEFINE_CLASS_1(ti_scm_syscon_reg, ti_scm_syscon_reg_class, ti_scm_syscon_reg_methods, + 0, syscon_class); + +/* device interface */ +static int +ti_scm_syscon_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "TI OMAP Control Module Syscon"); + return(BUS_PROBE_DEFAULT); +} + +static int +ti_scm_syscon_attach(device_t dev) +{ + struct ti_scm_syscon_softc *sc; + phandle_t node, child; + int err; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, ti_scm_syscon_res_spec, sc->res)) { + device_printf(sc->dev, "Cant allocate resources\n"); + return (ENXIO); + } + + sc->dev = dev; + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF); + node = ofw_bus_get_node(sc->dev); + + /* dev/extres/syscon interface */ + sc->syscon = syscon_create_ofw_node(dev, &ti_scm_syscon_reg_class, node); + if (sc->syscon == NULL) { + device_printf(dev, "Failed to create/register syscon\n"); + return (ENXIO); + } + + simplebus_init(sc->dev, node); + + err = bus_generic_probe(sc->dev); + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL); + } + + return (bus_generic_attach(sc->dev)); +} + +/* syscon interface */ +static int +ti_scm_syscon_get_handle(device_t dev, struct syscon **syscon) +{ + struct ti_scm_syscon_softc *sc; + + sc = device_get_softc(dev); + *syscon = sc->syscon; + if (*syscon == NULL) + return (ENODEV); + return (0); +} + +/* clkdev interface */ +static int +ti_scm_syscon_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct ti_scm_syscon_softc *sc; + + sc = device_get_softc(dev); + DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val); + bus_space_write_4(sc->bst, sc->bsh, addr, val); + return (0); +} + +static int +ti_scm_syscon_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct ti_scm_syscon_softc *sc; + + sc = device_get_softc(dev); + + *val = bus_space_read_4(sc->bst, sc->bsh, addr); + DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val); + return (0); +} + +static int +ti_scm_syscon_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct ti_scm_syscon_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + reg = bus_space_read_4(sc->bst, sc->bsh, addr); + reg &= ~clr; + reg |= set; + bus_space_write_4(sc->bst, sc->bsh, addr, reg); + DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set); + + return (0); +} + +static void +ti_scm_syscon_clk_device_lock(device_t dev) +{ + struct ti_scm_syscon_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +ti_scm_syscon_clk_device_unlock(device_t dev) +{ + struct ti_scm_syscon_softc *sc; + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static device_method_t ti_scm_syscon_methods[] = { + DEVMETHOD(device_probe, ti_scm_syscon_probe), + DEVMETHOD(device_attach, ti_scm_syscon_attach), + + /* syscon interface */ + DEVMETHOD(syscon_get_handle, ti_scm_syscon_get_handle), + + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, ti_scm_syscon_clk_write_4), + DEVMETHOD(clkdev_read_4, ti_scm_syscon_clk_read_4), + DEVMETHOD(clkdev_modify_4, ti_scm_syscon_clk_modify_4), + DEVMETHOD(clkdev_device_lock, ti_scm_syscon_clk_device_lock), + DEVMETHOD(clkdev_device_unlock, ti_scm_syscon_clk_device_unlock), + + DEVMETHOD_END +}; + + +DEFINE_CLASS_1(ti_scm_syscon, ti_scm_syscon_driver, ti_scm_syscon_methods, + sizeof(struct ti_scm_syscon_softc), simplebus_driver); + +static devclass_t ti_scm_syscon_devclass; + +EARLY_DRIVER_MODULE(ti_scm_syscon, simplebus, ti_scm_syscon_driver, + ti_scm_syscon_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ti_scm_syscon, 1); +MODULE_DEPEND(ti_scm_syscon, ti_scm, 1, 1, 1); Property changes on: head/sys/arm/ti/ti_scm_syscon.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_sdhci.c =================================================================== --- head/sys/arm/ti/ti_sdhci.c (revision 363699) +++ head/sys/arm/ti/ti_sdhci.c (revision 363700) @@ -1,761 +1,770 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Ian Lepore * Copyright (c) 2011 Ben Gray . * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include +#include +#include +#include "gpio_if.h" +#include #include #include #include #include #include #include #include #include "sdhci_if.h" -#include -#include -#include -#include "gpio_if.h" +#include +#include +#include + #include "opt_mmccam.h" struct ti_sdhci_softc { device_t dev; struct sdhci_fdt_gpio * gpio; struct resource * mem_res; struct resource * irq_res; void * intr_cookie; struct sdhci_slot slot; - clk_ident_t mmchs_clk_id; uint32_t mmchs_reg_off; uint32_t sdhci_reg_off; - uint32_t baseclk_hz; + uint64_t baseclk_hz; uint32_t cmd_and_mode; uint32_t sdhci_clkdiv; boolean_t disable_highspeed; boolean_t force_card_present; boolean_t disable_readonly; }; /* * Table of supported FDT compat strings. * * Note that "ti,mmchs" is our own invention, and should be phased out in favor * of the documented names. * * Note that vendor Beaglebone dtsi files use "ti,omap3-hsmmc" for the am335x. */ static struct ofw_compat_data compat_data[] = { {"ti,omap3-hsmmc", 1}, {"ti,omap4-hsmmc", 1}, {"ti,mmchs", 1}, {NULL, 0}, }; /* * The MMCHS hardware has a few control and status registers at the beginning of * the device's memory map, followed by the standard sdhci register block. * Different SoCs have the register blocks at different offsets from the * beginning of the device. Define some constants to map out the registers we * access, and the various per-SoC offsets. The SDHCI_REG_OFFSET is how far * beyond the MMCHS block the SDHCI block is found; it's the same on all SoCs. */ #define OMAP3_MMCHS_REG_OFFSET 0x000 #define OMAP4_MMCHS_REG_OFFSET 0x100 #define AM335X_MMCHS_REG_OFFSET 0x100 #define SDHCI_REG_OFFSET 0x100 #define MMCHS_SYSCONFIG 0x010 #define MMCHS_SYSCONFIG_RESET (1 << 1) #define MMCHS_SYSSTATUS 0x014 #define MMCHS_SYSSTATUS_RESETDONE (1 << 0) #define MMCHS_CON 0x02C #define MMCHS_CON_DW8 (1 << 5) #define MMCHS_CON_DVAL_8_4MS (3 << 9) #define MMCHS_CON_OD (1 << 0) #define MMCHS_SYSCTL 0x12C #define MMCHS_SYSCTL_CLKD_MASK 0x3FF #define MMCHS_SYSCTL_CLKD_SHIFT 6 #define MMCHS_SD_CAPA 0x140 #define MMCHS_SD_CAPA_VS18 (1 << 26) #define MMCHS_SD_CAPA_VS30 (1 << 25) #define MMCHS_SD_CAPA_VS33 (1 << 24) /* Forward declarations, CAM-relataed */ // static void ti_sdhci_cam_poll(struct cam_sim *); // static void ti_sdhci_cam_action(struct cam_sim *, union ccb *); // static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *); static inline uint32_t ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off + sc->mmchs_reg_off)); } static inline void ti_mmchs_write_4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off + sc->mmchs_reg_off, val); } static inline uint32_t RD4(struct ti_sdhci_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off + sc->sdhci_reg_off)); } static inline void WR4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off + sc->sdhci_reg_off, val); } static uint8_t ti_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xff); } static uint16_t ti_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t clkdiv, val32; /* * The MMCHS hardware has a non-standard interpretation of the sdclock * divisor bits. It uses the same bit positions as SDHCI 3.0 (15..6) * but doesn't split them into low:high fields. Instead they're a * single number in the range 0..1023 and the number is exactly the * clock divisor (with 0 and 1 both meaning divide by 1). The SDHCI * driver code expects a v2.0 or v3.0 divisor. The shifting and masking * here extracts the MMCHS representation from the hardware word, cleans * those bits out, applies the 2N adjustment, and plugs the result into * the bit positions for the 2.0 or 3.0 divisor in the returned register * value. The ti_sdhci_write_2() routine performs the opposite * transformation when the SDHCI driver writes to the register. */ if (off == SDHCI_CLOCK_CONTROL) { val32 = RD4(sc, SDHCI_CLOCK_CONTROL); clkdiv = ((val32 >> MMCHS_SYSCTL_CLKD_SHIFT) & MMCHS_SYSCTL_CLKD_MASK) / 2; val32 &= ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT); val32 |= (clkdiv & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT; if (slot->version >= SDHCI_SPEC_300) val32 |= ((clkdiv >> SDHCI_DIVIDER_MASK_LEN) & SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_HI_SHIFT; return (val32 & 0xffff); } /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { return (sc->cmd_and_mode >> 16); } else if (off == SDHCI_COMMAND_FLAGS) { return (sc->cmd_and_mode & 0x0000ffff); } return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xffff); } static uint32_t ti_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; val32 = RD4(sc, off); /* * If we need to disallow highspeed mode due to the OMAP4 erratum, strip * that flag from the returned capabilities. */ if (off == SDHCI_CAPABILITIES && sc->disable_highspeed) val32 &= ~SDHCI_CAN_DO_HISPD; /* * Force the card-present state if necessary. */ if (off == SDHCI_PRESENT_STATE && sc->force_card_present) val32 |= SDHCI_CARD_PRESENT; return (val32); } static void ti_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct ti_sdhci_softc *sc = device_get_softc(dev); bus_read_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count); } static void ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; #ifdef MMCCAM uint32_t newval32; if (off == SDHCI_HOST_CONTROL) { val32 = ti_mmchs_read_4(sc, MMCHS_CON); newval32 = val32; if (val & SDHCI_CTRL_8BITBUS) { device_printf(dev, "Custom-enabling 8-bit bus\n"); newval32 |= MMCHS_CON_DW8; } else { device_printf(dev, "Custom-disabling 8-bit bus\n"); newval32 &= ~MMCHS_CON_DW8; } if (newval32 != val32) ti_mmchs_write_4(sc, MMCHS_CON, newval32); } #endif val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3) * 8); val32 |= (val << (off & 3) * 8); WR4(sc, off & ~3, val32); } static void ti_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t clkdiv, val32; /* * Translate between the hardware and SDHCI 2.0 or 3.0 representations * of the clock divisor. See the comments in ti_sdhci_read_2() for * details. */ if (off == SDHCI_CLOCK_CONTROL) { clkdiv = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK; if (slot->version >= SDHCI_SPEC_300) clkdiv |= ((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_MASK_LEN; clkdiv *= 2; if (clkdiv > MMCHS_SYSCTL_CLKD_MASK) clkdiv = MMCHS_SYSCTL_CLKD_MASK; val32 = RD4(sc, SDHCI_CLOCK_CONTROL); val32 &= 0xffff0000; val32 |= val & ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT); val32 |= clkdiv << MMCHS_SYSCTL_CLKD_SHIFT; WR4(sc, SDHCI_CLOCK_CONTROL, val32); return; } /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { sc->cmd_and_mode = (sc->cmd_and_mode & 0xffff0000) | ((uint32_t)val & 0x0000ffff); return; } else if (off == SDHCI_COMMAND_FLAGS) { sc->cmd_and_mode = (sc->cmd_and_mode & 0x0000ffff) | ((uint32_t)val << 16); WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode); return; } val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3) * 8); val32 |= ((val & 0xffff) << (off & 3) * 8); WR4(sc, off & ~3, val32); } static void ti_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); } static void ti_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct ti_sdhci_softc *sc = device_get_softc(dev); bus_write_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count); } static void ti_sdhci_intr(void *arg) { struct ti_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->slot); } static int ti_sdhci_update_ios(device_t brdev, device_t reqdev) { struct ti_sdhci_softc *sc = device_get_softc(brdev); struct sdhci_slot *slot; struct mmc_ios *ios; uint32_t val32, newval32; slot = device_get_ivars(reqdev); ios = &slot->host.ios; /* * There is an 8-bit-bus bit in the MMCHS control register which, when * set, overrides the 1 vs 4 bit setting in the standard SDHCI * registers. Set that bit first according to whether an 8-bit bus is * requested, then let the standard driver handle everything else. */ val32 = ti_mmchs_read_4(sc, MMCHS_CON); newval32 = val32; if (ios->bus_width == bus_width_8) newval32 |= MMCHS_CON_DW8; else newval32 &= ~MMCHS_CON_DW8; if (ios->bus_mode == opendrain) newval32 |= MMCHS_CON_OD; else /* if (ios->bus_mode == pushpull) */ newval32 &= ~MMCHS_CON_OD; if (newval32 != val32) ti_mmchs_write_4(sc, MMCHS_CON, newval32); return (sdhci_generic_update_ios(brdev, reqdev)); } static int ti_sdhci_get_ro(device_t brdev, device_t reqdev) { struct ti_sdhci_softc *sc = device_get_softc(brdev); if (sc->disable_readonly) return (0); return (sdhci_fdt_gpio_get_readonly(sc->gpio)); } static bool ti_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot) { struct ti_sdhci_softc *sc = device_get_softc(dev); return (sdhci_fdt_gpio_get_present(sc->gpio)); } static int ti_sdhci_detach(device_t dev) { /* sdhci_fdt_gpio_teardown(sc->gpio); */ return (EBUSY); } -static void +static int ti_sdhci_hw_init(device_t dev) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t regval; unsigned long timeout; + clk_t mmc_clk; + int err; /* Enable the controller and interface/functional clocks */ - if (ti_prcm_clk_enable(sc->mmchs_clk_id) != 0) { + if (ti_sysc_clock_enable(device_get_parent(dev)) != 0) { device_printf(dev, "Error: failed to enable MMC clock\n"); - return; + return (ENXIO); } - /* Get the frequency of the source clock */ - if (ti_prcm_clk_get_source_freq(sc->mmchs_clk_id, - &sc->baseclk_hz) != 0) { - device_printf(dev, "Error: failed to get source clock freq\n"); - return; + /* FIXME: Devicetree dosent have any reference to mmc_clk */ + err = clk_get_by_name(dev, "mmc_clk", &mmc_clk); + if (err) { + device_printf(dev, "Can not find mmc_clk\n"); + return (ENXIO); } + err = clk_get_freq(mmc_clk, &sc->baseclk_hz); + if (err) { + device_printf(dev, "Cant get mmc_clk frequency\n"); + /* AM335x TRM 8.1.6.8 table 8-24 96MHz @ OPP100 */ + sc->baseclk_hz = 96000000; + } /* Issue a softreset to the controller */ ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET); timeout = 1000; while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSSTATUS_RESETDONE)) { if (--timeout == 0) { device_printf(dev, "Error: Controller reset operation timed out\n"); break; } DELAY(100); } /* * Reset the command and data state machines and also other aspects of * the controller such as bus clock and power. * * If we read the software reset register too fast after writing it we * can get back a zero that means the reset hasn't started yet rather * than that the reset is complete. Per TI recommendations, work around * it by reading until we see the reset bit asserted, then read until * it's clear. We also set the SDHCI_QUIRK_WAITFOR_RESET_ASSERTED quirk * so that the main sdhci driver uses this same logic in its resets. */ ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL); timeout = 10000; while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) != SDHCI_RESET_ALL) { if (--timeout == 0) { break; } DELAY(1); } timeout = 10000; while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL)) { if (--timeout == 0) { device_printf(dev, "Error: Software reset operation timed out\n"); break; } DELAY(100); } /* * The attach() routine has examined fdt data and set flags in * slot.host.caps to reflect what voltages we can handle. Set those * values in the CAPA register. Empirical testing shows that the * values in this register can be overwritten at any time, but the * manual says that these values should only be set once, "before * initialization" whatever that means, and that they survive a reset. */ regval = ti_mmchs_read_4(sc, MMCHS_SD_CAPA); if (sc->slot.host.caps & MMC_OCR_LOW_VOLTAGE) regval |= MMCHS_SD_CAPA_VS18; if (sc->slot.host.caps & (MMC_OCR_290_300 | MMC_OCR_300_310)) regval |= MMCHS_SD_CAPA_VS30; ti_mmchs_write_4(sc, MMCHS_SD_CAPA, regval); /* Set initial host configuration (1-bit, std speed, pwr off). */ ti_sdhci_write_1(dev, NULL, SDHCI_HOST_CONTROL, 0); ti_sdhci_write_1(dev, NULL, SDHCI_POWER_CONTROL, 0); /* Set the initial controller configuration. */ ti_mmchs_write_4(sc, MMCHS_CON, MMCHS_CON_DVAL_8_4MS); + + return (0); } static int ti_sdhci_attach(device_t dev) { struct ti_sdhci_softc *sc = device_get_softc(dev); int rid, err; pcell_t prop; phandle_t node; sc->dev = dev; /* - * Get the MMCHS device id from FDT. If it's not there use the newbus - * unit number (which will work as long as the devices are in order and - * none are skipped in the fdt). Note that this is a property we made - * up and added in freebsd, it doesn't exist in the published bindings. + * Get the MMCHS device id from FDT. Use rev address to identify the unit. */ node = ofw_bus_get_node(dev); - sc->mmchs_clk_id = ti_hwmods_get_clock(dev); - if (sc->mmchs_clk_id == INVALID_CLK_IDENT) { - device_printf(dev, "failed to get clock based on hwmods property\n"); - } /* * The hardware can inherently do dual-voltage (1p8v, 3p0v) on the first * device, and only 1p8v on other devices unless an external transceiver * is used. The only way we could know about a transceiver is fdt data. * Note that we have to do this before calling ti_sdhci_hw_init() so * that it can set the right values in the CAPA register. */ sc->slot.host.caps |= MMC_OCR_LOW_VOLTAGE; - if (sc->mmchs_clk_id == MMC1_CLK || OF_hasprop(node, "ti,dual-volt")) { + + if (OF_hasprop(node, "ti,dual-volt")) { sc->slot.host.caps |= MMC_OCR_290_300 | MMC_OCR_300_310; } /* * Set the offset from the device's memory start to the MMCHS registers. * Also for OMAP4 disable high speed mode due to erratum ID i626. */ switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: sc->mmchs_reg_off = OMAP4_MMCHS_REG_OFFSET; sc->disable_highspeed = true; break; #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: sc->mmchs_reg_off = AM335X_MMCHS_REG_OFFSET; break; #endif default: panic("Unknown OMAP device\n"); } /* * The standard SDHCI registers are at a fixed offset (the same on all * SoCs) beyond the MMCHS registers. */ sc->sdhci_reg_off = sc->mmchs_reg_off + SDHCI_REG_OFFSET; /* Resource setup. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, ti_sdhci_intr, sc, &sc->intr_cookie)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } /* * Set up handling of card-detect and write-protect gpio lines. * * If there is no write protect info in the fdt data, fall back to the * historical practice of assuming that the card is writable. This * works around bad fdt data from the upstream source. The alternative * would be to trust the sdhci controller's PRESENT_STATE register WP * bit, but it may say write protect is in effect when it's not if the * pinmux setup doesn't route the WP signal into the sdchi block. */ sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot); if (!OF_hasprop(node, "wp-gpios") && !OF_hasprop(node, "wp-disable")) sc->disable_readonly = true; /* Initialise the MMCHS hardware. */ - ti_sdhci_hw_init(dev); + err = ti_sdhci_hw_init(dev); + if (err != 0) { + /* err should already contain ENXIO from ti_sdhci_hw_init() */ + goto fail; + } /* * The capabilities register can only express base clock frequencies in * the range of 0-63MHz for a v2.0 controller. Since our clock runs * faster than that, the hardware sets the frequency to zero in the * register. When the register contains zero, the sdhci driver expects * slot.max_clk to already have the right value in it. */ sc->slot.max_clk = sc->baseclk_hz; /* * The MMCHS timeout counter is based on the output sdclock. Tell the * sdhci driver to recalculate the timeout clock whenever the output * sdclock frequency changes. */ sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * The MMCHS hardware shifts the 136-bit response data (in violation of * the spec), so tell the sdhci driver not to do the same in software. */ sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE; /* * Reset bits are broken, have to wait to see the bits asserted * before waiting to see them de-asserted. */ sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED; /* * The controller waits for busy responses. */ sc->slot.quirks |= SDHCI_QUIRK_WAIT_WHILE_BUSY; /* * DMA is not really broken, I just haven't implemented it yet. */ sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA; /* * Set up the hardware and go. Note that this sets many of the * slot.host.* fields, so we have to do this before overriding any of * those values based on fdt data, below. */ sdhci_init_slot(dev, &sc->slot, 0); /* * The SDHCI controller doesn't realize it, but we can support 8-bit * even though we're not a v3.0 controller. If there's an fdt bus-width * property, honor it. */ if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) { sc->slot.host.caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA); switch (prop) { case 8: sc->slot.host.caps |= MMC_CAP_8_BIT_DATA; /* FALLTHROUGH */ case 4: sc->slot.host.caps |= MMC_CAP_4_BIT_DATA; break; case 1: break; default: device_printf(dev, "Bad bus-width value %u\n", prop); break; } } /* * If the slot is flagged with the non-removable property, set our flag * to always force the SDHCI_CARD_PRESENT bit on. */ node = ofw_bus_get_node(dev); if (OF_hasprop(node, "non-removable")) sc->force_card_present = true; bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->slot); return (0); fail: if (sc->intr_cookie) bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); if (sc->irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (err); } static int ti_sdhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "TI MMCHS (SDHCI 2.0)"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static device_method_t ti_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_sdhci_probe), DEVMETHOD(device_attach, ti_sdhci_attach), DEVMETHOD(device_detach, ti_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, ti_sdhci_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, ti_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, ti_sdhci_read_1), DEVMETHOD(sdhci_read_2, ti_sdhci_read_2), DEVMETHOD(sdhci_read_4, ti_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, ti_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, ti_sdhci_write_1), DEVMETHOD(sdhci_write_2, ti_sdhci_write_2), DEVMETHOD(sdhci_write_4, ti_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, ti_sdhci_write_multi_4), DEVMETHOD(sdhci_get_card_present, ti_sdhci_get_card_present), DEVMETHOD_END }; static devclass_t ti_sdhci_devclass; static driver_t ti_sdhci_driver = { "sdhci_ti", ti_sdhci_methods, sizeof(struct ti_sdhci_softc), }; DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL, NULL); +MODULE_DEPEND(sdhci_ti, ti_sysc, 1, 1, 1); SDHCI_DEPEND(sdhci_ti); #ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhci_ti); #endif Index: head/sys/arm/ti/ti_sdma.c =================================================================== --- head/sys/arm/ti/ti_sdma.c (revision 363699) +++ head/sys/arm/ti/ti_sdma.c (revision 363700) @@ -1,1251 +1,1252 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 * Ben Gray . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include /** * Kernel functions for using the DMA controller * * * DMA TRANSFERS: * A DMA transfer block consists of a number of frames (FN). Each frame * consists of a number of elements, and each element can have a size of 8, 16, * or 32 bits. * * OMAP44xx and newer chips support linked list (aka scatter gather) transfers, * where a linked list of source/destination pairs can be placed in memory * for the H/W to process. Earlier chips only allowed you to chain multiple * channels together. However currently this linked list feature is not * supported by the driver. * */ /** * Data structure per DMA channel. * * */ struct ti_sdma_channel { - /* + /* * The configuration registers for the given channel, these are modified * by the set functions and only written to the actual registers when a * transaction is started. */ uint32_t reg_csdp; uint32_t reg_ccr; uint32_t reg_cicr; /* Set when one of the configuration registers above change */ uint32_t need_reg_write; /* Callback function used when an interrupt is tripped on the given channel */ void (*callback)(unsigned int ch, uint32_t ch_status, void *data); /* Callback data passed in the callback ... duh */ void* callback_data; }; /** * DMA driver context, allocated and stored globally, this driver is not * intetned to ever be unloaded (see ti_sdma_sc). * */ struct ti_sdma_softc { device_t sc_dev; struct resource* sc_irq_res; struct resource* sc_mem_res; - /* + /* * I guess in theory we should have a mutex per DMA channel for register * modifications. But since we know we are never going to be run on a SMP * system, we can use just the single lock for all channels. */ struct mtx sc_mtx; /* Stores the H/W revision read from the registers */ uint32_t sc_hw_rev; - /* + /* * Bits in the sc_active_channels data field indicate if the channel has * been activated. */ uint32_t sc_active_channels; struct ti_sdma_channel sc_channel[NUM_DMA_CHANNELS]; }; static struct ti_sdma_softc *ti_sdma_sc = NULL; /** * Macros for driver mutex locking */ #define TI_SDMA_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define TI_SDMA_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define TI_SDMA_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "ti_sdma", MTX_SPIN) #define TI_SDMA_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define TI_SDMA_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define TI_SDMA_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); /** * Function prototypes * */ static void ti_sdma_intr(void *); /** * ti_sdma_read_4 - reads a 32-bit value from one of the DMA registers * @sc: DMA device context * @off: The offset of a register from the DMA register address range * * * RETURNS: * 32-bit value read from the register. */ static inline uint32_t ti_sdma_read_4(struct ti_sdma_softc *sc, bus_size_t off) { return bus_read_4(sc->sc_mem_res, off); } /** * ti_sdma_write_4 - writes a 32-bit value to one of the DMA registers * @sc: DMA device context * @off: The offset of a register from the DMA register address range * * * RETURNS: * 32-bit value read from the register. */ static inline void ti_sdma_write_4(struct ti_sdma_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res, off, val); } /** * ti_sdma_is_omap3_rev - returns true if H/W is from OMAP3 series * @sc: DMA device context * */ static inline int ti_sdma_is_omap3_rev(struct ti_sdma_softc *sc) { return (sc->sc_hw_rev == DMA4_OMAP3_REV); } /** * ti_sdma_is_omap4_rev - returns true if H/W is from OMAP4 series * @sc: DMA device context * */ static inline int ti_sdma_is_omap4_rev(struct ti_sdma_softc *sc) { return (sc->sc_hw_rev == DMA4_OMAP4_REV); } /** * ti_sdma_intr - interrupt handler for all 4 DMA IRQs * @arg: ignored * * Called when any of the four DMA IRQs are triggered. * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * nothing */ static void ti_sdma_intr(void *arg) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t intr; uint32_t csr; unsigned int ch, j; struct ti_sdma_channel* channel; TI_SDMA_LOCK(sc); for (j = 0; j < NUM_DMA_IRQS; j++) { /* Get the flag interrupts (enabled) */ intr = ti_sdma_read_4(sc, DMA4_IRQSTATUS_L(j)); intr &= ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j)); if (intr == 0x00000000) continue; /* Loop through checking the status bits */ for (ch = 0; ch < NUM_DMA_CHANNELS; ch++) { if (intr & (1 << ch)) { channel = &sc->sc_channel[ch]; /* Read the CSR regsiter and verify we don't have a spurious IRQ */ csr = ti_sdma_read_4(sc, DMA4_CSR(ch)); if (csr == 0) { device_printf(sc->sc_dev, "Spurious DMA IRQ for channel " "%d\n", ch); continue; } /* Sanity check this channel is active */ if ((sc->sc_active_channels & (1 << ch)) == 0) { device_printf(sc->sc_dev, "IRQ %d for a non-activated " "channel %d\n", j, ch); continue; } /* Check the status error codes */ if (csr & DMA4_CSR_DROP) device_printf(sc->sc_dev, "Synchronization event drop " "occurred during the transfer on channel %u\n", ch); if (csr & DMA4_CSR_SECURE_ERR) device_printf(sc->sc_dev, "Secure transaction error event " "on channel %u\n", ch); if (csr & DMA4_CSR_MISALIGNED_ADRS_ERR) device_printf(sc->sc_dev, "Misaligned address error event " "on channel %u\n", ch); if (csr & DMA4_CSR_TRANS_ERR) { device_printf(sc->sc_dev, "Transaction error event on " "channel %u\n", ch); - /* + /* * Apparently according to linux code, there is an errata * that says the channel is not disabled upon this error. * They explicitly disable the channel here .. since I * haven't seen the errata, I'm going to ignore for now. */ } /* Clear the status flags for the IRQ */ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); /* Call the callback for the given channel */ if (channel->callback) channel->callback(ch, csr, channel->callback_data); } } } TI_SDMA_UNLOCK(sc); return; } /** * ti_sdma_activate_channel - activates a DMA channel * @ch: upon return contains the channel allocated * @callback: a callback function to associate with the channel * @data: optional data supplied when the callback is called * * Simply activates a channel be enabling and writing default values to the * channel's register set. It doesn't start a transaction, just populates the * internal data structures and sets defaults. * * Note this function doesn't enable interrupts, for that you need to call * ti_sdma_enable_channel_irq(). If not using IRQ to detect the end of the * transfer, you can use ti_sdma_status_poll() to detect a change in the * status. * * A channel must be activated before any of the other DMA functions can be * called on it. * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * 0 on success, otherwise an error code */ int ti_sdma_activate_channel(unsigned int *ch, void (*callback)(unsigned int ch, uint32_t status, void *data), void *data) { struct ti_sdma_softc *sc = ti_sdma_sc; struct ti_sdma_channel *channel = NULL; uint32_t addr; unsigned int i; /* Sanity check */ if (sc == NULL) return (ENOMEM); if (ch == NULL) return (EINVAL); TI_SDMA_LOCK(sc); /* Check to see if all channels are in use */ if (sc->sc_active_channels == 0xffffffff) { TI_SDMA_UNLOCK(sc); return (ENOMEM); } /* Find the first non-active channel */ for (i = 0; i < NUM_DMA_CHANNELS; i++) { if (!(sc->sc_active_channels & (0x1 << i))) { sc->sc_active_channels |= (0x1 << i); *ch = i; break; } } /* Get the channel struct and populate the fields */ channel = &sc->sc_channel[*ch]; channel->callback = callback; channel->callback_data = data; channel->need_reg_write = 1; /* Set the default configuration for the DMA channel */ channel->reg_csdp = DMA4_CSDP_DATA_TYPE(0x2) | DMA4_CSDP_SRC_BURST_MODE(0) | DMA4_CSDP_DST_BURST_MODE(0) | DMA4_CSDP_SRC_ENDIANISM(0) | DMA4_CSDP_DST_ENDIANISM(0) | DMA4_CSDP_WRITE_MODE(0) | DMA4_CSDP_SRC_PACKED(0) | DMA4_CSDP_DST_PACKED(0); channel->reg_ccr = DMA4_CCR_DST_ADDRESS_MODE(1) | DMA4_CCR_SRC_ADDRESS_MODE(1) | DMA4_CCR_READ_PRIORITY(0) | DMA4_CCR_WRITE_PRIORITY(0) | DMA4_CCR_SYNC_TRIGGER(0) | DMA4_CCR_FRAME_SYNC(0) | DMA4_CCR_BLOCK_SYNC(0); channel->reg_cicr = DMA4_CICR_TRANS_ERR_IE | DMA4_CICR_SECURE_ERR_IE | DMA4_CICR_SUPERVISOR_ERR_IE | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; /* Clear all the channel registers, this should abort any transaction */ for (addr = DMA4_CCR(*ch); addr <= DMA4_COLOR(*ch); addr += 4) ti_sdma_write_4(sc, addr, 0x00000000); TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_deactivate_channel - deactivates a channel * @ch: the channel to deactivate * * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_deactivate_channel(unsigned int ch) { struct ti_sdma_softc *sc = ti_sdma_sc; unsigned int j; unsigned int addr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); /* First check if the channel is currently active */ if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EBUSY); } /* Mark the channel as inactive */ sc->sc_active_channels &= ~(1 << ch); /* Disable all DMA interrupts for the channel. */ ti_sdma_write_4(sc, DMA4_CICR(ch), 0); /* Make sure the DMA transfer is stopped. */ ti_sdma_write_4(sc, DMA4_CCR(ch), 0); /* Clear the CSR register and IRQ status register */ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); for (j = 0; j < NUM_DMA_IRQS; j++) { ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); } /* Clear all the channel registers, this should abort any transaction */ for (addr = DMA4_CCR(ch); addr <= DMA4_COLOR(ch); addr += 4) ti_sdma_write_4(sc, addr, 0x00000000); TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_disable_channel_irq - disables IRQ's on the given channel * @ch: the channel to disable IRQ's on * * Disable interrupt generation for the given channel. * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_disable_channel_irq(unsigned int ch) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t irq_enable; unsigned int j; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } /* Disable all the individual error conditions */ sc->sc_channel[ch].reg_cicr = 0x0000; ti_sdma_write_4(sc, DMA4_CICR(ch), 0x0000); /* Disable the channel interrupt enable */ for (j = 0; j < NUM_DMA_IRQS; j++) { irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(j)); irq_enable &= ~(1 << ch); ti_sdma_write_4(sc, DMA4_IRQENABLE_L(j), irq_enable); } /* Indicate the registers need to be rewritten on the next transaction */ sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return (0); } /** * ti_sdma_disable_channel_irq - enables IRQ's on the given channel * @ch: the channel to enable IRQ's on * @flags: bitmask of interrupt types to enable * * Flags can be a bitmask of the following options: * DMA_IRQ_FLAG_DROP * DMA_IRQ_FLAG_HALF_FRAME_COMPL * DMA_IRQ_FLAG_FRAME_COMPL * DMA_IRQ_FLAG_START_LAST_FRAME * DMA_IRQ_FLAG_BLOCK_COMPL * DMA_IRQ_FLAG_ENDOF_PKT * DMA_IRQ_FLAG_DRAIN * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_enable_channel_irq(unsigned int ch, uint32_t flags) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t irq_enable; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } /* Always enable the error interrupts if we have interrupts enabled */ flags |= DMA4_CICR_TRANS_ERR_IE | DMA4_CICR_SECURE_ERR_IE | DMA4_CICR_SUPERVISOR_ERR_IE | DMA4_CICR_MISALIGNED_ADRS_ERR_IE; sc->sc_channel[ch].reg_cicr = flags; /* Write the values to the register */ ti_sdma_write_4(sc, DMA4_CICR(ch), flags); /* Enable the channel interrupt enable */ irq_enable = ti_sdma_read_4(sc, DMA4_IRQENABLE_L(0)); irq_enable |= (1 << ch); ti_sdma_write_4(sc, DMA4_IRQENABLE_L(0), irq_enable); /* Indicate the registers need to be rewritten on the next transaction */ sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return (0); } /** * ti_sdma_get_channel_status - returns the status of a given channel * @ch: the channel number to get the status of * @status: upon return will contain the status bitmask, see below for possible * values. * * DMA_STATUS_DROP * DMA_STATUS_HALF * DMA_STATUS_FRAME * DMA_STATUS_LAST * DMA_STATUS_BLOCK * DMA_STATUS_SYNC * DMA_STATUS_PKT * DMA_STATUS_TRANS_ERR * DMA_STATUS_SECURE_ERR * DMA_STATUS_SUPERVISOR_ERR * DMA_STATUS_MISALIGNED_ADRS_ERR * DMA_STATUS_DRAIN_END * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_get_channel_status(unsigned int ch, uint32_t *status) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t csr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } TI_SDMA_UNLOCK(sc); csr = ti_sdma_read_4(sc, DMA4_CSR(ch)); if (status != NULL) *status = csr; return (0); } /** * ti_sdma_start_xfer - starts a DMA transfer * @ch: the channel number to set the endianness of * @src_paddr: the source phsyical address * @dst_paddr: the destination phsyical address * @frmcnt: the number of frames per block * @elmcnt: the number of elements in a frame, an element is either an 8, 16 * or 32-bit value as defined by ti_sdma_set_xfer_burst() * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_start_xfer(unsigned int ch, unsigned int src_paddr, unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt) { struct ti_sdma_softc *sc = ti_sdma_sc; struct ti_sdma_channel *channel; uint32_t ccr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } channel = &sc->sc_channel[ch]; /* a) Write the CSDP register */ ti_sdma_write_4(sc, DMA4_CSDP(ch), channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); /* b) Set the number of element per frame CEN[23:0] */ ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt); /* c) Set the number of frame per block CFN[15:0] */ ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt); /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr); ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr); /* e) Write the CCR register */ ti_sdma_write_4(sc, DMA4_CCR(ch), channel->reg_ccr); /* f) - Set the source element index increment CSEI[15:0] */ ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001); /* - Set the source frame index increment CSFI[15:0] */ ti_sdma_write_4(sc, DMA4_CSF(ch), 0x0001); /* - Set the destination element index increment CDEI[15:0]*/ ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001); /* - Set the destination frame index increment CDFI[31:0] */ ti_sdma_write_4(sc, DMA4_CDF(ch), 0x0001); /* Clear the status register */ ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE); /* Write the start-bit and away we go */ ccr = ti_sdma_read_4(sc, DMA4_CCR(ch)); ccr |= (1 << 7); ti_sdma_write_4(sc, DMA4_CCR(ch), ccr); /* Clear the reg write flag */ channel->need_reg_write = 0; TI_SDMA_UNLOCK(sc); return (0); } /** * ti_sdma_start_xfer_packet - starts a packet DMA transfer * @ch: the channel number to use for the transfer * @src_paddr: the source physical address * @dst_paddr: the destination physical address * @frmcnt: the number of frames to transfer * @elmcnt: the number of elements in a frame, an element is either an 8, 16 * or 32-bit value as defined by ti_sdma_set_xfer_burst() * @pktsize: the number of elements in each transfer packet * * The @frmcnt and @elmcnt define the overall number of bytes to transfer, * typically @frmcnt is 1 and @elmcnt contains the total number of elements. * @pktsize is the size of each individual packet, there might be multiple * packets per transfer. i.e. for the following with element size of 32-bits * * frmcnt = 1, elmcnt = 512, pktsize = 128 * * Total transfer bytes = 1 * 512 = 512 elements or 2048 bytes * Packets transferred = 128 / 512 = 4 * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_start_xfer_packet(unsigned int ch, unsigned int src_paddr, unsigned long dst_paddr, unsigned int frmcnt, unsigned int elmcnt, unsigned int pktsize) { struct ti_sdma_softc *sc = ti_sdma_sc; struct ti_sdma_channel *channel; uint32_t ccr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } channel = &sc->sc_channel[ch]; /* a) Write the CSDP register */ if (channel->need_reg_write) ti_sdma_write_4(sc, DMA4_CSDP(ch), channel->reg_csdp | DMA4_CSDP_WRITE_MODE(1)); /* b) Set the number of elements to transfer CEN[23:0] */ ti_sdma_write_4(sc, DMA4_CEN(ch), elmcnt); /* c) Set the number of frames to transfer CFN[15:0] */ ti_sdma_write_4(sc, DMA4_CFN(ch), frmcnt); /* d) Set the Source/dest start address index CSSA[31:0]/CDSA[31:0] */ ti_sdma_write_4(sc, DMA4_CSSA(ch), src_paddr); ti_sdma_write_4(sc, DMA4_CDSA(ch), dst_paddr); /* e) Write the CCR register */ ti_sdma_write_4(sc, DMA4_CCR(ch), channel->reg_ccr | DMA4_CCR_PACKET_TRANS); /* f) - Set the source element index increment CSEI[15:0] */ ti_sdma_write_4(sc, DMA4_CSE(ch), 0x0001); /* - Set the packet size, this is dependent on the sync source */ if (channel->reg_ccr & DMA4_CCR_SEL_SRC_DST_SYNC(1)) ti_sdma_write_4(sc, DMA4_CSF(ch), pktsize); else ti_sdma_write_4(sc, DMA4_CDF(ch), pktsize); /* - Set the destination frame index increment CDFI[31:0] */ ti_sdma_write_4(sc, DMA4_CDE(ch), 0x0001); /* Clear the status register */ ti_sdma_write_4(sc, DMA4_CSR(ch), 0x1FFE); /* Write the start-bit and away we go */ ccr = ti_sdma_read_4(sc, DMA4_CCR(ch)); ccr |= (1 << 7); ti_sdma_write_4(sc, DMA4_CCR(ch), ccr); /* Clear the reg write flag */ channel->need_reg_write = 0; TI_SDMA_UNLOCK(sc); return (0); } /** * ti_sdma_stop_xfer - stops any currently active transfers * @ch: the channel number to set the endianness of * * This function call is effectively a NOP if no transaction is in progress. * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_stop_xfer(unsigned int ch) { struct ti_sdma_softc *sc = ti_sdma_sc; unsigned int j; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } /* Disable all DMA interrupts for the channel. */ ti_sdma_write_4(sc, DMA4_CICR(ch), 0); /* Make sure the DMA transfer is stopped. */ ti_sdma_write_4(sc, DMA4_CCR(ch), 0); /* Clear the CSR register and IRQ status register */ ti_sdma_write_4(sc, DMA4_CSR(ch), DMA4_CSR_CLEAR_MASK); for (j = 0; j < NUM_DMA_IRQS; j++) { ti_sdma_write_4(sc, DMA4_IRQSTATUS_L(j), (1 << ch)); } /* Configuration registers need to be re-written on the next xfer */ sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return (0); } /** * ti_sdma_set_xfer_endianess - sets the endianness of subsequent transfers * @ch: the channel number to set the endianness of * @src: the source endianness (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) * @dst: the destination endianness (either DMA_ENDIAN_LITTLE or DMA_ENDIAN_BIG) * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_set_xfer_endianess(unsigned int ch, unsigned int src, unsigned int dst) { struct ti_sdma_softc *sc = ti_sdma_sc; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_ENDIANISM(1); sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_ENDIANISM(src); sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_ENDIANISM(1); sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_ENDIANISM(dst); sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_set_xfer_burst - sets the source and destination element size * @ch: the channel number to set the burst settings of * @src: the source endianness (either DMA_BURST_NONE, DMA_BURST_16, DMA_BURST_32 * or DMA_BURST_64) * @dst: the destination endianness (either DMA_BURST_NONE, DMA_BURST_16, * DMA_BURST_32 or DMA_BURST_64) * * This function sets the size of the elements for all subsequent transfers. * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_set_xfer_burst(unsigned int ch, unsigned int src, unsigned int dst) { struct ti_sdma_softc *sc = ti_sdma_sc; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_SRC_BURST_MODE(0x3); sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_SRC_BURST_MODE(src); sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DST_BURST_MODE(0x3); sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DST_BURST_MODE(dst); sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_set_xfer_data_type - driver attach function * @ch: the channel number to set the endianness of * @type: the xfer data type (either DMA_DATA_8BITS_SCALAR, DMA_DATA_16BITS_SCALAR * or DMA_DATA_32BITS_SCALAR) * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_set_xfer_data_type(unsigned int ch, unsigned int type) { struct ti_sdma_softc *sc = ti_sdma_sc; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } sc->sc_channel[ch].reg_csdp &= ~DMA4_CSDP_DATA_TYPE(0x3); sc->sc_channel[ch].reg_csdp |= DMA4_CSDP_DATA_TYPE(type); sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_set_callback - driver attach function * @dev: dma device handle * * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_set_callback(unsigned int ch, void (*callback)(unsigned int ch, uint32_t status, void *data), void *data) { struct ti_sdma_softc *sc = ti_sdma_sc; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } sc->sc_channel[ch].callback = callback; sc->sc_channel[ch].callback_data = data; sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_sync_params - sets channel sync settings * @ch: the channel number to set the sync on * @trigger: the number of the sync trigger, this depends on what other H/W * module is triggering/receiving the DMA transactions * @mode: flags describing the sync mode to use, it may have one or more of * the following bits set; TI_SDMA_SYNC_FRAME, * TI_SDMA_SYNC_BLOCK, TI_SDMA_SYNC_TRIG_ON_SRC. * * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_sync_params(unsigned int ch, unsigned int trigger, unsigned int mode) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t ccr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } ccr = sc->sc_channel[ch].reg_ccr; ccr &= ~DMA4_CCR_SYNC_TRIGGER(0x7F); ccr |= DMA4_CCR_SYNC_TRIGGER(trigger + 1); if (mode & TI_SDMA_SYNC_FRAME) ccr |= DMA4_CCR_FRAME_SYNC(1); else ccr &= ~DMA4_CCR_FRAME_SYNC(1); if (mode & TI_SDMA_SYNC_BLOCK) ccr |= DMA4_CCR_BLOCK_SYNC(1); else ccr &= ~DMA4_CCR_BLOCK_SYNC(1); if (mode & TI_SDMA_SYNC_TRIG_ON_SRC) ccr |= DMA4_CCR_SEL_SRC_DST_SYNC(1); else ccr &= ~DMA4_CCR_SEL_SRC_DST_SYNC(1); sc->sc_channel[ch].reg_ccr = ccr; sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_set_addr_mode - driver attach function * @ch: the channel number to set the endianness of * @rd_mode: the xfer source addressing mode (either DMA_ADDR_CONSTANT, * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or * DMA_ADDR_DOUBLE_INDEX) * @wr_mode: the xfer destination addressing mode (either DMA_ADDR_CONSTANT, * DMA_ADDR_POST_INCREMENT, DMA_ADDR_SINGLE_INDEX or * DMA_ADDR_DOUBLE_INDEX) * * * LOCKING: * DMA registers protected by internal mutex * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ int ti_sdma_set_addr_mode(unsigned int ch, unsigned int src_mode, unsigned int dst_mode) { struct ti_sdma_softc *sc = ti_sdma_sc; uint32_t ccr; /* Sanity check */ if (sc == NULL) return (ENOMEM); TI_SDMA_LOCK(sc); if ((sc->sc_active_channels & (1 << ch)) == 0) { TI_SDMA_UNLOCK(sc); return (EINVAL); } ccr = sc->sc_channel[ch].reg_ccr; ccr &= ~DMA4_CCR_SRC_ADDRESS_MODE(0x3); ccr |= DMA4_CCR_SRC_ADDRESS_MODE(src_mode); ccr &= ~DMA4_CCR_DST_ADDRESS_MODE(0x3); ccr |= DMA4_CCR_DST_ADDRESS_MODE(dst_mode); sc->sc_channel[ch].reg_ccr = ccr; sc->sc_channel[ch].need_reg_write = 1; TI_SDMA_UNLOCK(sc); return 0; } /** * ti_sdma_probe - driver probe function * @dev: dma device handle * * * * RETURNS: * Always returns 0. */ static int ti_sdma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,omap4430-sdma")) return (ENXIO); device_set_desc(dev, "TI sDMA Controller"); return (0); } /** * ti_sdma_attach - driver attach function * @dev: dma device handle * * Initialises memory mapping/pointers to the DMA register set and requests * IRQs. This is effectively the setup function for the driver. * * RETURNS: * 0 on success or a negative error code failure. */ static int ti_sdma_attach(device_t dev) { struct ti_sdma_softc *sc = device_get_softc(dev); unsigned int timeout; unsigned int i; int rid; void *ihl; int err; /* Setup the basics */ sc->sc_dev = dev; /* No channels active at the moment */ sc->sc_active_channels = 0x00000000; /* Mutex to protect the shared data structures */ TI_SDMA_LOCK_INIT(sc); /* Get the memory resource for the register mapping */ rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) panic("%s: Cannot map registers", device_get_name(dev)); /* Enable the interface and functional clocks */ - ti_prcm_clk_enable(SDMA_CLK); + ti_sysc_clock_enable(device_get_parent(dev)); /* Read the sDMA revision register and sanity check it's known */ - sc->sc_hw_rev = ti_sdma_read_4(sc, DMA4_REVISION); + sc->sc_hw_rev = ti_sdma_read_4(sc, + ti_sysc_get_rev_address_offset_host(device_get_parent(dev))); device_printf(dev, "sDMA revision %08x\n", sc->sc_hw_rev); if (!ti_sdma_is_omap4_rev(sc) && !ti_sdma_is_omap3_rev(sc)) { device_printf(sc->sc_dev, "error - unknown sDMA H/W revision\n"); return (EINVAL); } /* Disable all interrupts */ for (i = 0; i < NUM_DMA_IRQS; i++) { ti_sdma_write_4(sc, DMA4_IRQENABLE_L(i), 0x00000000); } /* Soft-reset is only supported on pre-OMAP44xx devices */ if (ti_sdma_is_omap3_rev(sc)) { /* Soft-reset */ ti_sdma_write_4(sc, DMA4_OCP_SYSCONFIG, 0x0002); /* Set the timeout to 100ms*/ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); /* Wait for DMA reset to complete */ while ((ti_sdma_read_4(sc, DMA4_SYSSTATUS) & 0x1) == 0x0) { /* Sleep for a tick */ pause("DMARESET", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "sDMA reset operation timed out\n"); return (EINVAL); } } } - /* + /* * Install interrupt handlers for the for possible interrupts. Any channel * can trip one of the four IRQs */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) panic("Unable to setup the dma irq handler.\n"); err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_sdma_intr, NULL, &ihl); if (err) panic("%s: Cannot register IRQ", device_get_name(dev)); /* Store the DMA structure globally ... this driver should never be unloaded */ ti_sdma_sc = sc; return (0); } static device_method_t ti_sdma_methods[] = { DEVMETHOD(device_probe, ti_sdma_probe), DEVMETHOD(device_attach, ti_sdma_attach), {0, 0}, }; static driver_t ti_sdma_driver = { "ti_sdma", ti_sdma_methods, sizeof(struct ti_sdma_softc), }; static devclass_t ti_sdma_devclass; DRIVER_MODULE(ti_sdma, simplebus, ti_sdma_driver, ti_sdma_devclass, 0, 0); -MODULE_DEPEND(ti_sdma, ti_prcm, 1, 1, 1); +MODULE_DEPEND(ti_sdma, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/ti_spi.c =================================================================== --- head/sys/arm/ti/ti_spi.c (revision 363699) +++ head/sys/arm/ti/ti_spi.c (revision 363700) @@ -1,594 +1,582 @@ /*- * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include #include #include #include "spibus_if.h" static void ti_spi_intr(void *); static int ti_spi_detach(device_t); #undef TI_SPI_DEBUG #ifdef TI_SPI_DEBUG #define IRQSTATUSBITS \ "\020\1TX0_EMPTY\2TX0_UNDERFLOW\3RX0_FULL\4RX0_OVERFLOW" \ "\5TX1_EMPTY\6TX1_UNDERFLOW\7RX1_FULL\11TX2_EMPTY" \ "\12TX1_UNDERFLOW\13RX2_FULL\15TX3_EMPTY\16TX3_UNDERFLOW" \ "\17RX3_FULL\22EOW" #define CONFBITS \ "\020\1PHA\2POL\7EPOL\17DMAW\20DMAR\21DPE0\22DPE1\23IS" \ "\24TURBO\25FORCE\30SBE\31SBPOL\34FFEW\35FFER\36CLKG" #define STATBITS \ "\020\1RXS\2TXS\3EOT\4TXFFE\5TXFFF\6RXFFE\7RXFFFF" #define MODULCTRLBITS \ "\020\1SINGLE\2NOSPIEN\3SLAVE\4SYST\10MOA\11FDAA" #define CTRLBITS \ "\020\1ENABLED" static void ti_spi_printr(device_t dev) { int clk, conf, ctrl, div, i, j, wl; struct ti_spi_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = TI_SPI_READ(sc, MCSPI_SYSCONFIG); device_printf(dev, "SYSCONFIG: %#x\n", reg); reg = TI_SPI_READ(sc, MCSPI_SYSSTATUS); device_printf(dev, "SYSSTATUS: %#x\n", reg); reg = TI_SPI_READ(sc, MCSPI_IRQSTATUS); device_printf(dev, "IRQSTATUS: 0x%b\n", reg, IRQSTATUSBITS); reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); device_printf(dev, "IRQENABLE: 0x%b\n", reg, IRQSTATUSBITS); reg = TI_SPI_READ(sc, MCSPI_MODULCTRL); device_printf(dev, "MODULCTRL: 0x%b\n", reg, MODULCTRLBITS); for (i = 0; i < sc->sc_numcs; i++) { ctrl = TI_SPI_READ(sc, MCSPI_CTRL_CH(i)); conf = TI_SPI_READ(sc, MCSPI_CONF_CH(i)); device_printf(dev, "CH%dCONF: 0x%b\n", i, conf, CONFBITS); if (conf & MCSPI_CONF_CLKG) { div = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK; div |= ((ctrl >> MCSPI_CTRL_EXTCLK_SHIFT) & MCSPI_CTRL_EXTCLK_MSK) << 4; } else { div = 1; j = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK; while (j-- > 0) div <<= 1; } clk = TI_SPI_GCLK / div; wl = ((conf >> MCSPI_CONF_WL_SHIFT) & MCSPI_CONF_WL_MSK) + 1; device_printf(dev, "wordlen: %-2d clock: %d\n", wl, clk); reg = TI_SPI_READ(sc, MCSPI_STAT_CH(i)); device_printf(dev, "CH%dSTAT: 0x%b\n", i, reg, STATBITS); device_printf(dev, "CH%dCTRL: 0x%b\n", i, ctrl, CTRLBITS); } reg = TI_SPI_READ(sc, MCSPI_XFERLEVEL); device_printf(dev, "XFERLEVEL: %#x\n", reg); } #endif static void ti_spi_set_clock(struct ti_spi_softc *sc, int ch, int freq) { uint32_t clkdiv, conf, div, extclk, reg; clkdiv = TI_SPI_GCLK / freq; if (clkdiv > MCSPI_EXTCLK_MSK) { extclk = 0; clkdiv = 0; div = 1; while (TI_SPI_GCLK / div > freq && clkdiv <= 0xf) { clkdiv++; div <<= 1; } conf = clkdiv << MCSPI_CONF_CLK_SHIFT; } else { extclk = clkdiv >> 4; clkdiv &= MCSPI_CONF_CLK_MSK; conf = MCSPI_CONF_CLKG | clkdiv << MCSPI_CONF_CLK_SHIFT; } reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(ch)); reg &= ~(MCSPI_CTRL_EXTCLK_MSK << MCSPI_CTRL_EXTCLK_SHIFT); reg |= extclk << MCSPI_CTRL_EXTCLK_SHIFT; TI_SPI_WRITE(sc, MCSPI_CTRL_CH(ch), reg); reg = TI_SPI_READ(sc, MCSPI_CONF_CH(ch)); reg &= ~(MCSPI_CONF_CLKG | MCSPI_CONF_CLK_MSK << MCSPI_CONF_CLK_SHIFT); TI_SPI_WRITE(sc, MCSPI_CONF_CH(ch), reg | conf); } static int ti_spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,omap4-mcspi")) return (ENXIO); device_set_desc(dev, "TI McSPI controller"); return (BUS_PROBE_DEFAULT); } static int ti_spi_attach(device_t dev) { - int clk_id, err, i, rid, timeout; + int err, i, rid, timeout; struct ti_spi_softc *sc; uint32_t rev; sc = device_get_softc(dev); sc->sc_dev = dev; - /* - * Get the MMCHS device id from FDT. If it's not there use the newbus - * unit number (which will work as long as the devices are in order and - * none are skipped in the fdt). Note that this is a property we made - * up and added in freebsd, it doesn't exist in the published bindings. - */ - clk_id = ti_hwmods_get_clock(dev); - if (clk_id == INVALID_CLK_IDENT) { - device_printf(dev, - "failed to get clock based on hwmods property\n"); - return (EINVAL); - } - /* Activate the McSPI module. */ - err = ti_prcm_clk_enable(clk_id); + err = ti_sysc_clock_enable(device_get_parent(dev)); if (err) { device_printf(dev, "Error: failed to activate source clock\n"); return (err); } /* Get the number of available channels. */ if ((OF_getencprop(ofw_bus_get_node(dev), "ti,spi-num-cs", &sc->sc_numcs, sizeof(sc->sc_numcs))) <= 0) { sc->sc_numcs = 2; } rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_spi_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "ti_spi", NULL, MTX_DEF); /* Issue a softreset to the controller */ TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); timeout = 1000; while (!(TI_SPI_READ(sc, MCSPI_SYSSTATUS) & MCSPI_SYSSTATUS_RESETDONE)) { if (--timeout == 0) { device_printf(dev, "Error: Controller reset operation timed out\n"); ti_spi_detach(dev); return (ENXIO); } DELAY(100); } /* Print the McSPI module revision. */ - rev = TI_SPI_READ(sc, MCSPI_REVISION); + rev = TI_SPI_READ(sc, + ti_sysc_get_rev_address_offset_host(device_get_parent(dev))); device_printf(dev, "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n", (rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK, (rev >> MCSPI_REVISION_FUNC_SHIFT) & MCSPI_REVISION_FUNC_MSK, (rev >> MCSPI_REVISION_RTL_SHIFT) & MCSPI_REVISION_RTL_MSK, (rev >> MCSPI_REVISION_MAJOR_SHIFT) & MCSPI_REVISION_MAJOR_MSK, (rev >> MCSPI_REVISION_MINOR_SHIFT) & MCSPI_REVISION_MINOR_MSK, (rev >> MCSPI_REVISION_CUSTOM_SHIFT) & MCSPI_REVISION_CUSTOM_MSK); /* Set Master mode, single channel. */ TI_SPI_WRITE(sc, MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE); /* Clear pending interrupts and disable interrupts. */ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0x0); TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff); for (i = 0; i < sc->sc_numcs; i++) { /* * Default to SPI mode 0, CS active low, 8 bits word length and * 500kHz clock. */ TI_SPI_WRITE(sc, MCSPI_CONF_CH(i), MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | (8 - 1) << MCSPI_CONF_WL_SHIFT); /* Set initial clock - 500kHz. */ ti_spi_set_clock(sc, i, 500000); } #ifdef TI_SPI_DEBUG ti_spi_printr(dev); #endif device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int ti_spi_detach(device_t dev) { struct ti_spi_softc *sc; sc = device_get_softc(dev); /* Clear pending interrupts and disable interrupts. */ TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0); TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff); /* Reset controller. */ TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET); bus_generic_detach(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static int ti_spi_fill_fifo(struct ti_spi_softc *sc) { int bytes, timeout; struct spi_command *cmd; uint32_t written; uint8_t *data; cmd = sc->sc_cmd; bytes = min(sc->sc_len - sc->sc_written, sc->sc_fifolvl); while (bytes-- > 0) { data = (uint8_t *)cmd->tx_cmd; written = sc->sc_written++; if (written >= cmd->tx_cmd_sz) { data = (uint8_t *)cmd->tx_data; written -= cmd->tx_cmd_sz; } if (sc->sc_fifolvl == 1) { /* FIFO disabled. */ timeout = 1000; while (--timeout > 0 && (TI_SPI_READ(sc, MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_TXS) == 0) { DELAY(100); } if (timeout == 0) return (-1); } TI_SPI_WRITE(sc, MCSPI_TX_CH(sc->sc_cs), data[written]); } return (0); } static int ti_spi_drain_fifo(struct ti_spi_softc *sc) { int bytes, timeout; struct spi_command *cmd; uint32_t read; uint8_t *data; cmd = sc->sc_cmd; bytes = min(sc->sc_len - sc->sc_read, sc->sc_fifolvl); while (bytes-- > 0) { data = (uint8_t *)cmd->rx_cmd; read = sc->sc_read++; if (read >= cmd->rx_cmd_sz) { data = (uint8_t *)cmd->rx_data; read -= cmd->rx_cmd_sz; } if (sc->sc_fifolvl == 1) { /* FIFO disabled. */ timeout = 1000; while (--timeout > 0 && (TI_SPI_READ(sc, MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_RXS) == 0) { DELAY(100); } if (timeout == 0) return (-1); } data[read] = TI_SPI_READ(sc, MCSPI_RX_CH(sc->sc_cs)); } return (0); } static void ti_spi_intr(void *arg) { int eow; struct ti_spi_softc *sc; uint32_t status; eow = 0; sc = (struct ti_spi_softc *)arg; TI_SPI_LOCK(sc); status = TI_SPI_READ(sc, MCSPI_IRQSTATUS); /* * No new TX_empty or RX_full event will be asserted while the CPU has * not performed the number of writes or reads defined by * MCSPI_XFERLEVEL[AEL] and MCSPI_XFERLEVEL[AFL]. It is responsibility * of CPU perform the right number of writes and reads. */ if (status & MCSPI_IRQ_TX0_EMPTY) ti_spi_fill_fifo(sc); if (status & MCSPI_IRQ_RX0_FULL) ti_spi_drain_fifo(sc); if (status & MCSPI_IRQ_EOW) eow = 1; /* Clear interrupt status. */ TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, status); /* Check for end of transfer. */ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { sc->sc_flags |= TI_SPI_DONE; wakeup(sc->sc_dev); } TI_SPI_UNLOCK(sc); } static int ti_spi_pio_transfer(struct ti_spi_softc *sc) { while (sc->sc_len - sc->sc_written > 0) { if (ti_spi_fill_fifo(sc) == -1) return (EIO); if (ti_spi_drain_fifo(sc) == -1) return (EIO); } return (0); } static int ti_spi_gcd(int a, int b) { int m; while ((m = a % b) != 0) { a = b; b = m; } return (b); } static int ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { int err; struct ti_spi_softc *sc; uint32_t clockhz, cs, mode, reg; sc = device_get_softc(dev); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("TX/RX command sizes should be equal")); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); spibus_get_clock(child, &clockhz); spibus_get_mode(child, &mode); cs &= ~SPIBUS_CS_HIGH; if (cs > sc->sc_numcs) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, device_get_nameunit(child)); return (EINVAL); } if (mode > 3) { device_printf(dev, "Invalid mode %d requested by %s\n", mode, device_get_nameunit(child)); return (EINVAL); } TI_SPI_LOCK(sc); /* If the controller is in use wait until it is available. */ while (sc->sc_flags & TI_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "ti_spi", 0); /* Now we have control over SPI controller. */ sc->sc_flags = TI_SPI_BUSY; /* Save the SPI command data. */ sc->sc_cs = cs; sc->sc_cmd = cmd; sc->sc_read = 0; sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; sc->sc_fifolvl = ti_spi_gcd(sc->sc_len, TI_SPI_FIFOSZ); if (sc->sc_fifolvl < 2 || sc->sc_len > 0xffff) sc->sc_fifolvl = 1; /* FIFO disabled. */ /* Disable FIFO for now. */ sc->sc_fifolvl = 1; /* Set the bus frequency. */ ti_spi_set_clock(sc, sc->sc_cs, clockhz); /* Disable the FIFO. */ TI_SPI_WRITE(sc, MCSPI_XFERLEVEL, 0); /* 8 bits word, d0 miso, d1 mosi, mode 0 and CS active low. */ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW | MCSPI_CONF_SBPOL | MCSPI_CONF_SBE | MCSPI_CONF_TURBO | MCSPI_CONF_IS | MCSPI_CONF_DPE1 | MCSPI_CONF_DPE0 | MCSPI_CONF_DMAR | MCSPI_CONF_DMAW | MCSPI_CONF_EPOL); reg |= MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | MCSPI_CONF_WL8BITS; reg |= mode; /* POL and PHA are the low bits, we can just OR-in mode */ TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); #if 0 /* Enable channel interrupts. */ reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); reg |= 0xf; TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg); #endif /* Start the transfer. */ reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs)); TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg | MCSPI_CTRL_ENABLE); /* Force CS on. */ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg |= MCSPI_CONF_FORCE); err = 0; if (sc->sc_fifolvl == 1) err = ti_spi_pio_transfer(sc); /* Force CS off. */ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); reg &= ~MCSPI_CONF_FORCE; TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); /* Disable IRQs. */ reg = TI_SPI_READ(sc, MCSPI_IRQENABLE); reg &= ~0xf; TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg); TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xf); /* Disable the SPI channel. */ reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs)); reg &= ~MCSPI_CTRL_ENABLE; TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg); /* Disable FIFO. */ reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs)); reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW); TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg); /* Release the controller and wakeup the next thread waiting for it. */ sc->sc_flags = 0; wakeup_one(dev); TI_SPI_UNLOCK(sc); return (err); } static phandle_t ti_spi_get_node(device_t bus, device_t dev) { /* Share controller node with spibus. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_spi_probe), DEVMETHOD(device_attach, ti_spi_attach), DEVMETHOD(device_detach, ti_spi_detach), /* SPI interface */ DEVMETHOD(spibus_transfer, ti_spi_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_spi_get_node), DEVMETHOD_END }; static devclass_t ti_spi_devclass; static driver_t ti_spi_driver = { "spi", ti_spi_methods, sizeof(struct ti_spi_softc), }; DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, ti_spi_devclass, 0, 0); +MODULE_DEPEND(ti_spi, ti_sysc, 1, 1, 1); Index: head/sys/arm/ti/ti_sysc.c =================================================================== --- head/sys/arm/ti/ti_sysc.c (revision 363699) +++ head/sys/arm/ti/ti_sysc.c (revision 363700) @@ -1,128 +1,622 @@ /*- * Copyright (c) 2019 Emmanuel Vadot * + * Copyright (c) 2020 Oskar Holmlund + * * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include +#include + +#include +#include + +#define DEBUG_SYSC 0 + +#if DEBUG_SYSC +#define DPRINTF(dev, msg...) device_printf(dev, msg) +#else +#define DPRINTF(dev, msg...) +#endif + +/* Documentation/devicetree/bindings/bus/ti-sysc.txt + * + * Documentation/devicetree/clock/clock-bindings.txt + * Defines phandle + optional pair + * Documentation/devicetree/clock/ti-clkctl.txt + */ + +static int ti_sysc_probe(device_t dev); +static int ti_sysc_attach(device_t dev); +static int ti_sysc_detach(device_t dev); + +#define TI_SYSC_DRA7_MCAN 15 +#define TI_SYSC_USB_HOST_FS 14 +#define TI_SYSC_DRA7_MCASP 13 +#define TI_SYSC_MCASP 12 +#define TI_SYSC_OMAP_AES 11 +#define TI_SYSC_OMAP3_SHAM 10 +#define TI_SYSC_OMAP4_SR 9 +#define TI_SYSC_OMAP3630_SR 8 +#define TI_SYSC_OMAP3430_SR 7 +#define TI_SYSC_OMAP4_TIMER 6 +#define TI_SYSC_OMAP2_TIMER 5 +/* Above needs special workarounds */ +#define TI_SYSC_OMAP4_SIMPLE 4 +#define TI_SYSC_OMAP4 3 +#define TI_SYSC_OMAP2 2 +#define TI_SYSC 1 +#define TI_SYSC_END 0 + static struct ofw_compat_data compat_data[] = { - { "ti,sysc", 1 }, - { NULL, 0 } + { "ti,sysc-dra7-mcan", TI_SYSC_DRA7_MCAN }, + { "ti,sysc-usb-host-fs", TI_SYSC_USB_HOST_FS }, + { "ti,sysc-dra7-mcasp", TI_SYSC_DRA7_MCASP }, + { "ti,sysc-mcasp", TI_SYSC_MCASP }, + { "ti,sysc-omap-aes", TI_SYSC_OMAP_AES }, + { "ti,sysc-omap3-sham", TI_SYSC_OMAP3_SHAM }, + { "ti,sysc-omap4-sr", TI_SYSC_OMAP4_SR }, + { "ti,sysc-omap3630-sr", TI_SYSC_OMAP3630_SR }, + { "ti,sysc-omap3430-sr", TI_SYSC_OMAP3430_SR }, + { "ti,sysc-omap4-timer", TI_SYSC_OMAP4_TIMER }, + { "ti,sysc-omap2-timer", TI_SYSC_OMAP2_TIMER }, + /* Above needs special workarounds */ + { "ti,sysc-omap4-simple", TI_SYSC_OMAP4_SIMPLE }, + { "ti,sysc-omap4", TI_SYSC_OMAP4 }, + { "ti,sysc-omap2", TI_SYSC_OMAP2 }, + { "ti,sysc", TI_SYSC }, + { NULL, TI_SYSC_END } }; +/* reg-names can be "rev", "sysc" and "syss" */ +static const char * reg_names[] = { "rev", "sysc", "syss" }; +#define REG_REV 0 +#define REG_SYSC 1 +#define REG_SYSS 2 +#define REG_MAX 3 + +/* master idle / slave idle mode defined in 8.1.3.2.1 / 8.1.3.2.2 */ +#include +#define SYSC_IDLE_MAX 4 + +struct sysc_reg { + uint64_t address; + uint64_t size; +}; + +struct clk_list { + TAILQ_ENTRY(clk_list) next; + clk_t clk; +}; + struct ti_sysc_softc { struct simplebus_softc sc; + bool attach_done; + device_t dev; + int device_type; + + struct sysc_reg reg[REG_MAX]; + /* Offset from host base address */ + uint64_t offset_reg[REG_MAX]; + + uint32_t ti_sysc_mask; + int32_t ti_sysc_midle[SYSC_IDLE_MAX]; + int32_t ti_sysc_sidle[SYSC_IDLE_MAX]; + uint32_t ti_sysc_delay_us; + uint32_t ti_syss_mask; + + int num_clocks; + TAILQ_HEAD(, clk_list) clk_list; + + /* deprecated ti_hwmods */ + bool ti_no_reset_on_init; + bool ti_no_idle_on_init; + bool ti_no_idle; }; -static int ti_sysc_probe(device_t dev); -static int ti_sysc_attach(device_t dev); -static int ti_sysc_detach(device_t dev); +/* + * All sysc seems to have a reg["rev"] register. + * Lets use that for identification of which module the driver are connected to. + */ +uint64_t +ti_sysc_get_rev_address(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + return (sc->reg[REG_REV].address); +} + +uint64_t +ti_sysc_get_rev_address_offset_host(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + + return (sc->offset_reg[REG_REV]); +} + +uint64_t +ti_sysc_get_sysc_address(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + + return (sc->reg[REG_SYSC].address); +} + +uint64_t +ti_sysc_get_sysc_address_offset_host(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + + return (sc->offset_reg[REG_SYSC]); +} + +uint64_t +ti_sysc_get_syss_address(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + + return (sc->reg[REG_SYSS].address); +} + +uint64_t +ti_sysc_get_syss_address_offset_host(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + + return (sc->offset_reg[REG_SYSS]); +} + +/* + * Due no memory region is assigned the sysc driver the children needs to + * handle the practical read/writes to the registers. + * Check if sysc has reset bit. + */ +uint32_t +ti_sysc_get_soft_reset_bit(device_t dev) { + struct ti_sysc_softc *sc = device_get_softc(dev); + switch (sc->device_type) { + case TI_SYSC_OMAP4_TIMER: + case TI_SYSC_OMAP4_SIMPLE: + case TI_SYSC_OMAP4: + if (sc->ti_sysc_mask & SYSC_OMAP4_SOFTRESET) { + return (SYSC_OMAP4_SOFTRESET); + } + break; + + case TI_SYSC_OMAP2_TIMER: + case TI_SYSC_OMAP2: + case TI_SYSC: + if (sc->ti_sysc_mask & SYSC_OMAP2_SOFTRESET) { + return (SYSC_OMAP2_SOFTRESET); + } + break; + default: + break; + } + + return (0); +} + +int +ti_sysc_clock_enable(device_t dev) { + struct clk_list *clkp, *clkp_tmp; + struct ti_sysc_softc *sc = device_get_softc(dev); + int err; + + TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { + err = clk_enable(clkp->clk); + + if (err) { + DPRINTF(sc->dev, "clk_enable %s failed %d\n", + clk_get_name(clkp->clk), err); + break; + } + } + return (err); +} + +int +ti_sysc_clock_disable(device_t dev) { + struct clk_list *clkp, *clkp_tmp; + struct ti_sysc_softc *sc = device_get_softc(dev); + int err = 0; + + TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { + err = clk_disable(clkp->clk); + + if (err) { + DPRINTF(sc->dev, "clk_enable %s failed %d\n", + clk_get_name(clkp->clk), err); + break; + } + } + return (err); +} + static int +parse_regfields(struct ti_sysc_softc *sc) { + phandle_t node; + uint32_t parent_address_cells; + uint32_t parent_size_cells; + cell_t *reg; + ssize_t nreg; + int err, k, reg_i, prop_idx; + uint32_t idx; + + node = ofw_bus_get_node(sc->dev); + + /* Get parents address and size properties */ + err = OF_searchencprop(OF_parent(node), "#address-cells", + &parent_address_cells, sizeof(parent_address_cells)); + if (err == -1) + return (ENXIO); + if (!(parent_address_cells == 1 || parent_address_cells == 2)) { + DPRINTF(sc->dev, "Expect parent #address-cells=[1||2]\n"); + return (ENXIO); + } + + err = OF_searchencprop(OF_parent(node), "#size-cells", + &parent_size_cells, sizeof(parent_size_cells)); + if (err == -1) + return (ENXIO); + + if (!(parent_size_cells == 1 || parent_size_cells == 2)) { + DPRINTF(sc->dev, "Expect parent #size-cells = [1||2]\n"); + return (ENXIO); + } + + /* Grab the content of reg properties */ + nreg = OF_getproplen(node, "reg"); + reg = malloc(nreg, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "reg", reg, nreg); + + /* Make sure address & size are 0 */ + for (idx = 0; idx < REG_MAX; idx++) { + sc->reg[idx].address = 0; + sc->reg[idx].size = 0; + } + + /* Loop through reg-names and figure out which reg-name corresponds to + * index populate the values into the reg array. + */ + for (idx = 0, reg_i = 0; idx < REG_MAX && reg_i < nreg; idx++) { + err = ofw_bus_find_string_index(node, "reg-names", + reg_names[idx], &prop_idx); + if (err != 0) + continue; + + for (k = 0; k < parent_address_cells; k++) { + sc->reg[prop_idx].address <<= 32; + sc->reg[prop_idx].address |= reg[reg_i++]; + } + + for (k = 0; k < parent_size_cells; k++) { + sc->reg[prop_idx].size <<= 32; + sc->reg[prop_idx].size |= reg[reg_i++]; + } + + if (sc->sc.nranges == 0) + sc->offset_reg[prop_idx] = sc->reg[prop_idx].address; + else + sc->offset_reg[prop_idx] = sc->reg[prop_idx].address - + sc->sc.ranges[REG_REV].host; + + DPRINTF(sc->dev, "reg[%s] adress %#jx size %#jx\n", + reg_names[idx], + sc->reg[prop_idx].address, + sc->reg[prop_idx].size); + } + free(reg, M_DEVBUF); + return (0); +} + +static void +parse_idle(struct ti_sysc_softc *sc, const char *name, uint32_t *idle) { + phandle_t node; + cell_t value[SYSC_IDLE_MAX]; + int len, no, i; + + node = ofw_bus_get_node(sc->dev); + + if (!OF_hasprop(node, name)) { + return; + } + + len = OF_getproplen(node, name); + no = len / sizeof(cell_t); + if (no >= SYSC_IDLE_MAX) { + DPRINTF(sc->dev, "Limit %s\n", name); + no = SYSC_IDLE_MAX-1; + len = no * sizeof(cell_t); + } + + OF_getencprop(node, name, value, len); + for (i = 0; i < no; i++) { + idle[i] = value[i]; +#if DEBUG_SYSC + DPRINTF(sc->dev, "%s[%d] = %d ", + name, i, value[i]); + switch(value[i]) { + case SYSC_IDLE_FORCE: + DPRINTF(sc->dev, "SYSC_IDLE_FORCE\n"); + break; + case SYSC_IDLE_NO: + DPRINTF(sc->dev, "SYSC_IDLE_NO\n"); + break; + case SYSC_IDLE_SMART: + DPRINTF(sc->dev, "SYSC_IDLE_SMART\n"); + break; + case SYSC_IDLE_SMART_WKUP: + DPRINTF(sc->dev, "SYSC_IDLE_SMART_WKUP\n"); + break; + } +#endif + } + for ( ; i < SYSC_IDLE_MAX; i++) + idle[i] = -1; +} + +static int +ti_sysc_attach_clocks(struct ti_sysc_softc *sc) { + clk_t *clk; + struct clk_list *clkp; + int index, err; + phandle_t cnode; + + clk = malloc(sc->num_clocks*sizeof(clk_t), M_DEVBUF, M_WAITOK | M_ZERO); + + cnode = ofw_bus_get_node(sc->dev); + + /* Check if all clocks can be found */ + for (index = 0; index < sc->num_clocks; index++) { + err = clk_get_by_ofw_index(sc->dev, 0, index, &clk[index]); + + if (err != 0) { + free(clk, M_DEVBUF); + return (1); + } + } + + /* All clocks are found, add to list */ + for (index = 0; index < sc->num_clocks; index++) { + clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); + clkp->clk = clk[index]; + TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); + } + + /* Release the clk array */ + free(clk, M_DEVBUF); + return (0); +} + +static int +ti_sysc_simplebus_attach_child(device_t dev) { + device_t cdev; + phandle_t node, child; + struct ti_sysc_softc *sc = device_get_softc(dev); + + node = ofw_bus_get_node(sc->dev); + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } + return (0); +} + +/* Device interface */ +static int ti_sysc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "TI SYSC Interconnect"); - if (!bootverbose) - device_quiet(dev); return (BUS_PROBE_DEFAULT); } static int ti_sysc_attach(device_t dev) { struct ti_sysc_softc *sc; - device_t cdev; - phandle_t node, child; + phandle_t node; + int err; + cell_t value; sc = device_get_softc(dev); sc->dev = dev; - node = ofw_bus_get_node(dev); + sc->device_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; - simplebus_init(dev, node); + node = ofw_bus_get_node(sc->dev); + /* ranges - use simplebus */ + simplebus_init(sc->dev, node); if (simplebus_fill_ranges(node, &sc->sc) < 0) { - device_printf(dev, "could not get ranges\n"); + DPRINTF(sc->dev, "could not get ranges\n"); return (ENXIO); } - for (child = OF_child(node); child > 0; child = OF_peer(child)) { - cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); - if (cdev != NULL) - device_probe_and_attach(cdev); + if (sc->sc.nranges == 0) { + DPRINTF(sc->dev, "nranges == 0\n"); + return (ENXIO); } - return (bus_generic_attach(dev)); + /* Required field reg & reg-names - assume at least "rev" exists */ + err = parse_regfields(sc); + if (err) { + DPRINTF(sc->dev, "parse_regfields failed %d\n", err); + return (ENXIO); + } + + /* Optional */ + if (OF_hasprop(node, "ti,sysc-mask")) { + OF_getencprop(node, "ti,sysc-mask", &value, sizeof(cell_t)); + sc->ti_sysc_mask = value; + } + if (OF_hasprop(node, "ti,syss-mask")) { + OF_getencprop(node, "ti,syss-mask", &value, sizeof(cell_t)); + sc->ti_syss_mask = value; + } + if (OF_hasprop(node, "ti,sysc-delay-us")) { + OF_getencprop(node, "ti,sysc-delay-us", &value, sizeof(cell_t)); + sc->ti_sysc_delay_us = value; + } + + DPRINTF(sc->dev, "sysc_mask %x syss_mask %x delay_us %x\n", + sc->ti_sysc_mask, sc->ti_syss_mask, sc->ti_sysc_delay_us); + + parse_idle(sc, "ti,sysc-midle", sc->ti_sysc_midle); + parse_idle(sc, "ti,sysc-sidle", sc->ti_sysc_sidle); + + if (OF_hasprop(node, "ti,no-reset-on-init")) + sc->ti_no_reset_on_init = true; + else + sc->ti_no_reset_on_init = false; + + if (OF_hasprop(node, "ti,no-idle-on-init")) + sc->ti_no_idle_on_init = true; + else + sc->ti_no_idle_on_init = false; + + if (OF_hasprop(node, "ti,no-idle")) + sc->ti_no_idle = true; + else + sc->ti_no_idle = false; + + DPRINTF(sc->dev, + "no-reset-on-init %d, no-idle-on-init %d, no-idle %d\n", + sc->ti_no_reset_on_init, + sc->ti_no_idle_on_init, + sc->ti_no_idle); + + if (OF_hasprop(node, "clocks")) { + struct clock_cell_info cell_info; + read_clock_cells(sc->dev, &cell_info); + free(cell_info.clock_cells, M_DEVBUF); + free(cell_info.clock_cells_ncells, M_DEVBUF); + + sc->num_clocks = cell_info.num_real_clocks; + TAILQ_INIT(&sc->clk_list); + + err = ti_sysc_attach_clocks(sc); + if (err) { + DPRINTF(sc->dev, "Failed to attach clocks\n"); + return (bus_generic_attach(sc->dev)); + } + } + + err = ti_sysc_simplebus_attach_child(sc->dev); + if (err) { + DPRINTF(sc->dev, "ti_sysc_simplebus_attach_child %d\n", + err); + return (err); + } + + sc->attach_done = true; + + return (bus_generic_attach(sc->dev)); } static int ti_sysc_detach(device_t dev) { - return (EBUSY); } +/* Bus interface */ +static void +ti_sysc_new_pass(device_t dev) +{ + struct ti_sysc_softc *sc; + int err; + phandle_t node; + + sc = device_get_softc(dev); + + if (sc->attach_done) { + bus_generic_new_pass(sc->dev); + return; + } + + node = ofw_bus_get_node(sc->dev); + if (OF_hasprop(node, "clocks")) { + err = ti_sysc_attach_clocks(sc); + if (err) { + DPRINTF(sc->dev, "Failed to attach clocks\n"); + return; + } + } + + err = ti_sysc_simplebus_attach_child(sc->dev); + if (err) { + DPRINTF(sc->dev, + "ti_sysc_simplebus_attach_child failed %d\n", err); + return; + } + sc->attach_done = true; + + bus_generic_attach(sc->dev); +} + static device_method_t ti_sysc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_sysc_probe), DEVMETHOD(device_attach, ti_sysc_attach), DEVMETHOD(device_detach, ti_sysc_detach), + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_sysc_new_pass), + DEVMETHOD_END }; DEFINE_CLASS_1(ti_sysc, ti_sysc_driver, ti_sysc_methods, - sizeof(struct ti_sysc_softc), simplebus_driver); + sizeof(struct ti_sysc_softc), simplebus_driver); static devclass_t ti_sysc_devclass; EARLY_DRIVER_MODULE(ti_sysc, simplebus, ti_sysc_driver, -ti_sysc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); + ti_sysc_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); Index: head/sys/arm/ti/ti_sysc.h =================================================================== --- head/sys/arm/ti/ti_sysc.h (nonexistent) +++ head/sys/arm/ti/ti_sysc.h (revision 363700) @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oskar Holmlund + * + * 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. + * + * $FreeBSD$ + */ +#ifndef __TI_SYSC__ +#define __TI_SYSC__ + +uint64_t ti_sysc_get_rev_address(device_t dev); +uint64_t ti_sysc_get_rev_address_offset_host(device_t dev); +uint64_t ti_sysc_get_sysc_address(device_t dev); +uint64_t ti_sysc_get_sysc_address_offset_host(device_t dev); +uint64_t ti_sysc_get_syss_address(device_t dev); +uint64_t ti_sysc_get_syss_address_offset_host(device_t dev); +int ti_sysc_clock_enable(device_t dev); +int ti_sysc_clock_disable(device_t dev); + +uint32_t ti_sysc_get_soft_reset_bit(device_t dev); + +#endif /* __TI_SYSC__ */ Property changes on: head/sys/arm/ti/ti_sysc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_wdt.c =================================================================== --- head/sys/arm/ti/ti_wdt.c (revision 363699) +++ head/sys/arm/ti/ti_wdt.c (revision 363700) @@ -1,276 +1,276 @@ /*- * Copyright (c) 2014 Rui Paulo * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #ifdef DEBUG #define DPRINTF(fmt, ...) do { \ printf("%s: ", __func__); \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(fmt, ...) #endif static device_probe_t ti_wdt_probe; static device_attach_t ti_wdt_attach; static device_detach_t ti_wdt_detach; static void ti_wdt_intr(void *); static void ti_wdt_event(void *, unsigned int, int *); struct ti_wdt_softc { struct resource *sc_mem_res; struct resource *sc_irq_res; void *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; eventhandler_tag sc_ev_tag; }; static device_method_t ti_wdt_methods[] = { DEVMETHOD(device_probe, ti_wdt_probe), DEVMETHOD(device_attach, ti_wdt_attach), DEVMETHOD(device_detach, ti_wdt_detach), DEVMETHOD_END }; static driver_t ti_wdt_driver = { "ti_wdt", ti_wdt_methods, sizeof(struct ti_wdt_softc) }; static devclass_t ti_wdt_devclass; DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0); +MODULE_DEPEND(ti_wdt, ti_sysc, 1, 1, 1); static __inline uint32_t ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } /* * Wait for the write to a specific synchronised register to complete. */ static __inline void ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit) { while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit) DELAY(10); } static __inline void ti_wdt_disable(struct ti_wdt_softc *sc) { DPRINTF("disabling watchdog %p\n", sc); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); } static __inline void ti_wdt_enable(struct ti_wdt_softc *sc) { DPRINTF("enabling watchdog %p\n", sc); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444); ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); } static int ti_wdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,omap3-wdt")) { device_set_desc(dev, "TI Watchdog Timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ti_wdt_attach(device_t dev) { struct ti_wdt_softc *sc; int rid; sc = device_get_softc(dev); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_mem_res); sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); ti_wdt_detach(dev); return (ENXIO); } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) { device_printf(dev, "unable to setup the interrupt handler\n"); ti_wdt_detach(dev); return (ENXIO); } /* Reset, enable interrupts and stop the watchdog. */ ti_wdt_reg_write(sc, TI_WDT_WDSC, ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR); while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR) DELAY(10); ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY); ti_wdt_disable(sc); if (bootverbose) device_printf(dev, "revision: 0x%x\n", ti_wdt_reg_read(sc, TI_WDT_WIDR)); sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc, 0); return (0); } static int ti_wdt_detach(device_t dev) { struct ti_wdt_softc *sc; sc = device_get_softc(dev); if (sc->sc_ev_tag) EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag); if (sc->sc_intr) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); return (0); } static void ti_wdt_intr(void *arg) { struct ti_wdt_softc *sc; sc = arg; DPRINTF("interrupt %p", sc); ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY); /* TODO: handle interrupt */ } static void ti_wdt_event(void *arg, unsigned int cmd, int *error) { struct ti_wdt_softc *sc; uint8_t s; uint32_t wldr; uint32_t ptv; sc = arg; ti_wdt_disable(sc); if (cmd == WD_TO_NEVER) { *error = 0; return; } DPRINTF("cmd 0x%x\n", cmd); cmd &= WD_INTERVAL; if (cmd < WD_TO_1SEC) { *error = EINVAL; return; } s = 1 << (cmd - WD_TO_1SEC); DPRINTF("seconds %u\n", s); /* * Leave the pre-scaler with its default values: * PTV = 0 == 2**0 == 1 * PRE = 1 (enabled) * * Compute the load register value assuming a 32kHz clock. * See OVF_Rate in the WDT section of the AM335x TRM. */ ptv = 0; wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1; DPRINTF("wldr 0x%x\n", wldr); ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr); /* * Trigger a timer reload. */ ti_wdt_reg_write(sc, TI_WDT_WTGR, ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1); ti_wdt_reg_wait(sc, TI_W_PEND_WTGR); ti_wdt_enable(sc); *error = 0; } Index: head/sys/arm/ti/usb/omap_ehci.c =================================================================== --- head/sys/arm/ti/usb/omap_ehci.c (revision 363699) +++ head/sys/arm/ti/usb/omap_ehci.c (revision 363700) @@ -1,472 +1,471 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2011 * Ben Gray . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include /* EHCI */ #define OMAP_USBHOST_HCCAPBASE 0x0000 #define OMAP_USBHOST_HCSPARAMS 0x0004 #define OMAP_USBHOST_HCCPARAMS 0x0008 #define OMAP_USBHOST_USBCMD 0x0010 #define OMAP_USBHOST_USBSTS 0x0014 #define OMAP_USBHOST_USBINTR 0x0018 #define OMAP_USBHOST_FRINDEX 0x001C #define OMAP_USBHOST_CTRLDSSEGMENT 0x0020 #define OMAP_USBHOST_PERIODICLISTBASE 0x0024 #define OMAP_USBHOST_ASYNCLISTADDR 0x0028 #define OMAP_USBHOST_CONFIGFLAG 0x0050 #define OMAP_USBHOST_PORTSC(i) (0x0054 + (0x04 * (i))) #define OMAP_USBHOST_INSNREG00 0x0090 #define OMAP_USBHOST_INSNREG01 0x0094 #define OMAP_USBHOST_INSNREG02 0x0098 #define OMAP_USBHOST_INSNREG03 0x009C #define OMAP_USBHOST_INSNREG04 0x00A0 #define OMAP_USBHOST_INSNREG05_UTMI 0x00A4 #define OMAP_USBHOST_INSNREG05_ULPI 0x00A4 #define OMAP_USBHOST_INSNREG06 0x00A8 #define OMAP_USBHOST_INSNREG07 0x00AC #define OMAP_USBHOST_INSNREG08 0x00B0 #define OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND (1 << 5) #define OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT 31 #define OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT 24 #define OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT 22 #define OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT 16 #define OMAP_USBHOST_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define OMAP_USBHOST_INSNREG05_ULPI_WRDATA_SHIFT 0 #define ULPI_FUNC_CTRL_RESET (1 << 5) /*-------------------------------------------------------------------------*/ /* * Macros for Set and Clear * See ULPI 1.1 specification to find the registers with Set and Clear offsets */ #define ULPI_SET(a) (a + 1) #define ULPI_CLR(a) (a + 2) /*-------------------------------------------------------------------------*/ /* * Register Map */ #define ULPI_VENDOR_ID_LOW 0x00 #define ULPI_VENDOR_ID_HIGH 0x01 #define ULPI_PRODUCT_ID_LOW 0x02 #define ULPI_PRODUCT_ID_HIGH 0x03 #define ULPI_FUNC_CTRL 0x04 #define ULPI_IFC_CTRL 0x07 #define ULPI_OTG_CTRL 0x0a #define ULPI_USB_INT_EN_RISE 0x0d #define ULPI_USB_INT_EN_FALL 0x10 #define ULPI_USB_INT_STS 0x13 #define ULPI_USB_INT_LATCH 0x14 #define ULPI_DEBUG 0x15 #define ULPI_SCRATCH 0x16 #define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller" struct omap_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ device_t sc_dev; }; static device_attach_t omap_ehci_attach; static device_detach_t omap_ehci_detach; /** * omap_ehci_read_4 - read a 32-bit value from the EHCI registers * omap_ehci_write_4 - write a 32-bit value from the EHCI registers * @sc: omap ehci device context * @off: byte offset within the register set to read from * @val: the value to write into the register * * * LOCKING: * None * * RETURNS: * nothing in case of write function, if read function returns the value read. */ static inline uint32_t omap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off) { return (bus_read_4(sc->base.sc_io_res, off)); } static inline void omap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->base.sc_io_res, off, val); } /** * omap_ehci_soft_phy_reset - resets the phy using the reset command * @isc: omap ehci device context * @port: port to send the reset over * * * LOCKING: * none * * RETURNS: * nothing */ static void omap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port) { unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); uint32_t reg; reg = ULPI_FUNC_CTRL_RESET /* FUNCTION_CTRL_SET register */ | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) /* Write */ | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) /* PORTn */ | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) /* start ULPI access*/ | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg); /* Wait for ULPI access completion */ while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI) & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { /* Sleep for a tick */ pause("USBPHY_RESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "PHY reset operation timed out\n"); break; } } } /** * omap_ehci_init - initialises the USB host EHCI controller * @isc: omap ehci device context * * This initialisation routine is quite heavily based on the work done by the * OMAP Linux team (for which I thank them very much). The init sequence is * almost identical, diverging only for the FreeBSD specifics. * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static int omap_ehci_init(struct omap_ehci_softc *isc) { uint32_t reg = 0; int i; device_t uhh_dev; uhh_dev = device_get_parent(isc->sc_dev); device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); /* Set the interrupt threshold control, it controls the maximum rate at * which the host controller issues interrupts. We set it to 1 microframe * at startup - the default is 8 mircoframes (equates to 1ms). */ reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); reg &= 0xff00ffff; reg |= (1 << 16); omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); /* Soft reset the PHY using PHY reset command over ULPI */ for (i = 0; i < OMAP_HS_USB_PORTS; i++) { if (omap_usb_port_mode(uhh_dev, i) == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, i); } return(0); } /** * omap_ehci_probe - starts the given command * @dev: * * Effectively boilerplate EHCI resume code. * * LOCKING: * Caller should be holding the OMAP3_MMC lock. * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ static int omap_ehci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,ehci-omap")) return (ENXIO); device_set_desc(dev, OMAP_EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } /** * omap_ehci_attach - driver entry point, sets up the ECHI controller/driver * @dev: the new device handle * * Sets up bus spaces, interrupt handles, etc for the EHCI controller. It also * parses the resource hints and calls omap_ehci_init() to initialise the * H/W. * * LOCKING: * none * * RETURNS: * 0 on success or a positive error code on failure. */ static int omap_ehci_attach(device_t dev) { struct omap_ehci_softc *isc = device_get_softc(dev); ehci_softc_t *sc = &isc->base; #ifdef SOC_OMAP4 phandle_t root; #endif int err; int rid; #ifdef SOC_OMAP4 /* * If we're running a Pandaboard, run Pandaboard-specific * init code. */ root = OF_finddevice("/"); if (ofw_bus_node_is_compatible(root, "ti,omap4-panda")) pandaboard_usb_hub_init(); #endif /* initialise some bus fields */ sc->sc_bus.parent = dev; sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = EHCI_MAX_DEVICES; sc->sc_bus.dma_bits = 32; sprintf(sc->sc_vendor, "Texas Instruments"); /* save the device */ isc->sc_dev = dev; /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) { return (ENOMEM); } /* Allocate resource for the EHCI register set */ rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(dev, "Error: Could not map EHCI memory\n"); goto error; } /* Request an interrupt resource */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Error: could not allocate irq\n"); goto error; } /* Add this device as a child of the USBus device */ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Error: could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR); /* Initialise the ECHI registers */ err = omap_ehci_init(isc); if (err) { device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); goto error; } /* Set the tag and size of the register set in the EHCI context */ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* Setup the interrupt */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { device_printf(dev, "Error: could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* Finally we are ready to kick off the ECHI host controller */ err = ehci_init(sc); if (err == 0) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(dev, "Error: USB init failed err=%d\n", err); goto error; } return (0); error: omap_ehci_detach(dev); return (ENXIO); } /** * omap_ehci_detach - detach the device and cleanup the driver * @dev: device handle * * Clean-up routine where everything initialised in omap_ehci_attach is * freed and cleaned up. This function calls omap_ehci_fini() to shutdown * the on-chip module. * * LOCKING: * none * * RETURNS: * Always returns 0 (success). */ static int omap_ehci_detach(device_t dev) { struct omap_ehci_softc *isc = device_get_softc(dev); ehci_softc_t *sc = &isc->base; int err; /* during module unload there are lots of children leftover */ device_delete_children(dev); /* * disable interrupts that might have been switched on in ehci_init */ if (sc->sc_io_res) { EWRITE4(sc, EHCI_USBINTR, 0); } if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); if (err) device_printf(dev, "Error: could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } /* Free the resources stored in the base EHCI handler */ if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res); sc->sc_io_res = NULL; } return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, omap_ehci_probe), DEVMETHOD(device_attach, omap_ehci_attach), DEVMETHOD(device_detach, omap_ehci_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(struct omap_ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(omap_ehci, omap_uhh, ehci_driver, ehci_devclass, 0, 0); Index: head/sys/arm/ti/usb/omap_host.c =================================================================== --- head/sys/arm/ti/usb/omap_host.c (revision 363699) +++ head/sys/arm/ti/usb/omap_host.c (revision 363700) @@ -1,465 +1,468 @@ /*- * Copyright (c) 2015 Oleksandr Tymoshenko * Copyright (c) 2011 Ben Gray . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include +#include #include /* * USB Host Module */ /* UHH */ #define OMAP_USBHOST_UHH_REVISION 0x0000 #define OMAP_USBHOST_UHH_SYSCONFIG 0x0010 #define OMAP_USBHOST_UHH_SYSSTATUS 0x0014 #define OMAP_USBHOST_UHH_HOSTCONFIG 0x0040 #define OMAP_USBHOST_UHH_DEBUG_CSR 0x0044 /* UHH Register Set */ #define UHH_SYSCONFIG_MIDLEMODE_MASK (3UL << 12) #define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY (2UL << 12) #define UHH_SYSCONFIG_MIDLEMODE_NOSTANDBY (1UL << 12) #define UHH_SYSCONFIG_MIDLEMODE_FORCESTANDBY (0UL << 12) #define UHH_SYSCONFIG_CLOCKACTIVITY (1UL << 8) #define UHH_SYSCONFIG_SIDLEMODE_MASK (3UL << 3) #define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE (2UL << 3) #define UHH_SYSCONFIG_SIDLEMODE_NOIDLE (1UL << 3) #define UHH_SYSCONFIG_SIDLEMODE_FORCEIDLE (0UL << 3) #define UHH_SYSCONFIG_ENAWAKEUP (1UL << 2) #define UHH_SYSCONFIG_SOFTRESET (1UL << 1) #define UHH_SYSCONFIG_AUTOIDLE (1UL << 0) #define UHH_HOSTCONFIG_APP_START_CLK (1UL << 31) #define UHH_HOSTCONFIG_P3_CONNECT_STATUS (1UL << 10) #define UHH_HOSTCONFIG_P2_CONNECT_STATUS (1UL << 9) #define UHH_HOSTCONFIG_P1_CONNECT_STATUS (1UL << 8) #define UHH_HOSTCONFIG_ENA_INCR_ALIGN (1UL << 5) #define UHH_HOSTCONFIG_ENA_INCR16 (1UL << 4) #define UHH_HOSTCONFIG_ENA_INCR8 (1UL << 3) #define UHH_HOSTCONFIG_ENA_INCR4 (1UL << 2) #define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN (1UL << 1) #define UHH_HOSTCONFIG_P1_ULPI_BYPASS (1UL << 0) /* The following are on rev2 (OMAP44xx) of the EHCI only */ #define UHH_SYSCONFIG_IDLEMODE_MASK (3UL << 2) #define UHH_SYSCONFIG_IDLEMODE_NOIDLE (1UL << 2) #define UHH_SYSCONFIG_STANDBYMODE_MASK (3UL << 4) #define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY (1UL << 4) #define UHH_HOSTCONFIG_P1_MODE_MASK (3UL << 16) #define UHH_HOSTCONFIG_P1_MODE_ULPI_PHY (0UL << 16) #define UHH_HOSTCONFIG_P1_MODE_UTMI_PHY (1UL << 16) #define UHH_HOSTCONFIG_P1_MODE_HSIC (3UL << 16) #define UHH_HOSTCONFIG_P2_MODE_MASK (3UL << 18) #define UHH_HOSTCONFIG_P2_MODE_ULPI_PHY (0UL << 18) #define UHH_HOSTCONFIG_P2_MODE_UTMI_PHY (1UL << 18) #define UHH_HOSTCONFIG_P2_MODE_HSIC (3UL << 18) /* * Values of UHH_REVISION - Note: these are not given in the TRM but taken * from the linux OMAP EHCI driver (thanks guys). It has been verified on * a Panda and Beagle board. */ #define OMAP_UHH_REV1 0x00000010 /* OMAP3 */ #define OMAP_UHH_REV2 0x50700100 /* OMAP4 */ struct omap_uhh_softc { struct simplebus_softc simplebus_sc; device_t sc_dev; /* UHH register set */ struct resource* uhh_mem_res; /* The revision of the HS USB HOST read from UHH_REVISION */ uint32_t uhh_rev; /* The following details are provided by conf hints */ int port_mode[3]; }; static device_attach_t omap_uhh_attach; static device_detach_t omap_uhh_detach; static inline uint32_t omap_uhh_read_4(struct omap_uhh_softc *sc, bus_size_t off) { return bus_read_4(sc->uhh_mem_res, off); } static inline void omap_uhh_write_4(struct omap_uhh_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->uhh_mem_res, off, val); } static int omap_uhh_init(struct omap_uhh_softc *isc) { uint8_t tll_ch_mask; uint32_t reg; int i; /* Enable Clocks for high speed USBHOST */ - ti_prcm_clk_enable(USBHSHOST_CLK); + ti_sysc_clock_enable(device_get_parent(isc->sc_dev)); /* Read the UHH revision */ isc->uhh_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->uhh_rev); + /* FIXME */ +#if 0 if (isc->uhh_rev == OMAP_UHH_REV2) { /* For OMAP44xx devices you have to enable the per-port clocks: * PHY_MODE - External ULPI clock * TTL_MODE - Internal UTMI clock * HSIC_MODE - Internal 480Mhz and 60Mhz clocks */ switch(isc->port_mode[0]) { case EHCI_HCD_OMAP_MODE_UNKNOWN: break; case EHCI_HCD_OMAP_MODE_PHY: if (ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK)) device_printf(isc->sc_dev, "failed to set clock source for port 0\n"); if (ti_prcm_clk_enable(USBP1_PHY_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP1_PHY_CLK source for port 0\n"); break; case EHCI_HCD_OMAP_MODE_TLL: if (ti_prcm_clk_enable(USBP1_UTMI_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP1_PHY_CLK source for port 0\n"); break; case EHCI_HCD_OMAP_MODE_HSIC: if (ti_prcm_clk_enable(USBP1_HSIC_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP1_PHY_CLK source for port 0\n"); break; default: device_printf(isc->sc_dev, "unknown port mode %d for port 0\n", isc->port_mode[0]); } switch(isc->port_mode[1]) { case EHCI_HCD_OMAP_MODE_UNKNOWN: break; case EHCI_HCD_OMAP_MODE_PHY: if (ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK)) device_printf(isc->sc_dev, "failed to set clock source for port 0\n"); if (ti_prcm_clk_enable(USBP2_PHY_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP2_PHY_CLK source for port 1\n"); break; case EHCI_HCD_OMAP_MODE_TLL: if (ti_prcm_clk_enable(USBP2_UTMI_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP2_UTMI_CLK source for port 1\n"); break; case EHCI_HCD_OMAP_MODE_HSIC: if (ti_prcm_clk_enable(USBP2_HSIC_CLK)) device_printf(isc->sc_dev, "failed to set clock USBP2_HSIC_CLK source for port 1\n"); break; default: device_printf(isc->sc_dev, "unknown port mode %d for port 1\n", isc->port_mode[1]); } } +#endif /* Put UHH in SmartIdle/SmartStandby mode */ reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); if (isc->uhh_rev == OMAP_UHH_REV1) { reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | UHH_SYSCONFIG_MIDLEMODE_MASK); reg |= (UHH_SYSCONFIG_ENAWAKEUP | UHH_SYSCONFIG_AUTOIDLE | UHH_SYSCONFIG_CLOCKACTIVITY | UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); } else if (isc->uhh_rev == OMAP_UHH_REV2) { reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); /* Setup ULPI bypass and burst configurations */ reg |= (UHH_HOSTCONFIG_ENA_INCR4 | UHH_HOSTCONFIG_ENA_INCR8 | UHH_HOSTCONFIG_ENA_INCR16); reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; if (isc->uhh_rev == OMAP_UHH_REV1) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; /* Bypass the TLL module for PHY mode operation */ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; else reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; } else if (isc->uhh_rev == OMAP_UHH_REV2) { reg |= UHH_HOSTCONFIG_APP_START_CLK; /* Clear port mode fields for PHY mode*/ reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); /* I found the code and comments in the Linux EHCI driver - thanks guys :) * * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared * (for example when we do omap_uhh_bus_suspend). This breaks suspend-resume if * the root-hub is allowed to suspend. Writing 1 to this undocumented * register bit disables this feature and restores normal behavior." */ #if 0 omap_uhh_write_4(isc, OMAP_USBHOST_INSNREG04, OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); #endif tll_ch_mask = 0; for (i = 0; i < OMAP_HS_USB_PORTS; i++) { if (isc->port_mode[i] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= (1 << i); } if (tll_ch_mask) omap_tll_utmi_enable(tll_ch_mask); return(0); } /** * omap_uhh_fini - shutdown the EHCI controller * @isc: omap ehci device context * * * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static void omap_uhh_fini(struct omap_uhh_softc *isc) { unsigned long timeout; device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); /* Set the timeout */ if (hz < 10) timeout = 1; else timeout = (100 * hz) / 1000; /* Reset the UHH, OHCI and EHCI modules */ omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "operation timed out\n"); break; } } /* Disable functional and interface clocks for the TLL and HOST modules */ - ti_prcm_clk_disable(USBHSHOST_CLK); + ti_sysc_clock_disable(device_get_parent(isc->sc_dev)); device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); } int omap_usb_port_mode(device_t dev, int port) { struct omap_uhh_softc *isc; isc = device_get_softc(dev); if ((port < 0) || (port >= OMAP_HS_USB_PORTS)) return (-1); return isc->port_mode[port]; } static int omap_uhh_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,usbhs-host")) return (ENXIO); device_set_desc(dev, "TI OMAP USB 2.0 Host module"); return (BUS_PROBE_DEFAULT); } static int omap_uhh_attach(device_t dev) { struct omap_uhh_softc *isc = device_get_softc(dev); int err; int rid; int i; phandle_t node; char propname[16]; char *mode; /* save the device */ isc->sc_dev = dev; /* Allocate resource for the UHH register set */ rid = 0; isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!isc->uhh_mem_res) { device_printf(dev, "Error: Could not map UHH memory\n"); goto error; } node = ofw_bus_get_node(dev); if (node == -1) goto error; /* Get port modes from FDT */ for (i = 0; i < OMAP_HS_USB_PORTS; i++) { isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; snprintf(propname, sizeof(propname), "port%d-mode", i+1); if (OF_getprop_alloc(node, propname, (void**)&mode) <= 0) continue; if (strcmp(mode, "ehci-phy") == 0) isc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY; else if (strcmp(mode, "ehci-tll") == 0) isc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL; else if (strcmp(mode, "ehci-hsic") == 0) isc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC; } /* Initialise the ECHI registers */ err = omap_uhh_init(isc); if (err) { device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); goto error; } simplebus_init(dev, node); /* * Allow devices to identify. */ bus_generic_probe(dev); /* * Now walk the OFW tree and attach top-level devices. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) simplebus_add_device(dev, node, 0, NULL, -1, NULL); return (bus_generic_attach(dev)); error: omap_uhh_detach(dev); return (ENXIO); } static int omap_uhh_detach(device_t dev) { struct omap_uhh_softc *isc = device_get_softc(dev); /* during module unload there are lots of children leftover */ device_delete_children(dev); if (isc->uhh_mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); isc->uhh_mem_res = NULL; } omap_uhh_fini(isc); return (0); } static device_method_t omap_uhh_methods[] = { /* Device interface */ DEVMETHOD(device_probe, omap_uhh_probe), DEVMETHOD(device_attach, omap_uhh_attach), DEVMETHOD(device_detach, omap_uhh_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; DEFINE_CLASS_1(omap_uhh, omap_uhh_driver, omap_uhh_methods, sizeof(struct omap_uhh_softc), simplebus_driver); static devclass_t omap_uhh_devclass; DRIVER_MODULE(omap_uhh, simplebus, omap_uhh_driver, omap_uhh_devclass, 0, 0); Index: head/sys/arm/ti/usb/omap_tll.c =================================================================== --- head/sys/arm/ti/usb/omap_tll.c (revision 363699) +++ head/sys/arm/ti/usb/omap_tll.c (revision 363700) @@ -1,363 +1,363 @@ /*- * Copyright (c) 2011 * Ben Gray . * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include -#include +#include #include /* * USB TLL Module */ #define OMAP_USBTLL_REVISION 0x0000 #define OMAP_USBTLL_SYSCONFIG 0x0010 #define OMAP_USBTLL_SYSSTATUS 0x0014 #define OMAP_USBTLL_IRQSTATUS 0x0018 #define OMAP_USBTLL_IRQENABLE 0x001C #define OMAP_USBTLL_TLL_SHARED_CONF 0x0030 #define OMAP_USBTLL_TLL_CHANNEL_CONF(i) (0x0040 + (0x04 * (i))) #define OMAP_USBTLL_SAR_CNTX(i) (0x0400 + (0x04 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_ID_LO(i) (0x0800 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_ID_HI(i) (0x0801 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_PRODUCT_ID_LO(i) (0x0802 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_PRODUCT_ID_HI(i) (0x0803 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_FUNCTION_CTRL(i) (0x0804 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_FUNCTION_CTRL_SET(i) (0x0805 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_FUNCTION_CTRL_CLR(i) (0x0806 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_INTERFACE_CTRL(i) (0x0807 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_INTERFACE_CTRL_SET(i) (0x0808 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_INTERFACE_CTRL_CLR(i) (0x0809 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_OTG_CTRL(i) (0x080A + (0x100 * (i))) #define OMAP_USBTLL_ULPI_OTG_CTRL_SET(i) (0x080B + (0x100 * (i))) #define OMAP_USBTLL_ULPI_OTG_CTRL_CLR(i) (0x080C + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_RISE(i) (0x080D + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_SET(i) (0x080E + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_RISE_CLR(i) (0x080F + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_FALL(i) (0x0810 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_SET(i) (0x0811 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_EN_FALL_CLR(i) (0x0812 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_STATUS(i) (0x0813 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_LATCH(i) (0x0814 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_DEBUG(i) (0x0815 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_SCRATCH_REGISTER(i) (0x0816 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_SET(i) (0x0817 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_SCRATCH_REGISTER_CLR(i) (0x0818 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_EXTENDED_SET_ACCESS(i) (0x082F + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN(i) (0x0830 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_SET(i) (0x0831 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VCONTROL_EN_CLR(i) (0x0832 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VCONTROL_STATUS(i) (0x0833 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VCONTROL_LATCH(i) (0x0834 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VSTATUS(i) (0x0835 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VSTATUS_SET(i) (0x0836 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_UTMI_VSTATUS_CLR(i) (0x0837 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_USB_INT_LATCH_NOCLR(i) (0x0838 + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_INT_EN(i) (0x083B + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_INT_EN_SET(i) (0x083C + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_INT_EN_CLR(i) (0x083D + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_INT_STATUS(i) (0x083E + (0x100 * (i))) #define OMAP_USBTLL_ULPI_VENDOR_INT_LATCH(i) (0x083F + (0x100 * (i))) /* TLL Register Set */ #define TLL_SYSCONFIG_CACTIVITY (1UL << 8) #define TLL_SYSCONFIG_SIDLE_SMART_IDLE (2UL << 3) #define TLL_SYSCONFIG_SIDLE_NO_IDLE (1UL << 3) #define TLL_SYSCONFIG_SIDLE_FORCED_IDLE (0UL << 3) #define TLL_SYSCONFIG_ENAWAKEUP (1UL << 2) #define TLL_SYSCONFIG_SOFTRESET (1UL << 1) #define TLL_SYSCONFIG_AUTOIDLE (1UL << 0) #define TLL_SYSSTATUS_RESETDONE (1UL << 0) #define TLL_SHARED_CONF_USB_90D_DDR_EN (1UL << 6) #define TLL_SHARED_CONF_USB_180D_SDR_EN (1UL << 5) #define TLL_SHARED_CONF_USB_DIVRATIO_MASK (7UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_128 (7UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_64 (6UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_32 (5UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_16 (4UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_8 (3UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_4 (2UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_2 (1UL << 2) #define TLL_SHARED_CONF_USB_DIVRATIO_1 (0UL << 2) #define TLL_SHARED_CONF_FCLK_REQ (1UL << 1) #define TLL_SHARED_CONF_FCLK_IS_ON (1UL << 0) #define TLL_CHANNEL_CONF_DRVVBUS (1UL << 16) #define TLL_CHANNEL_CONF_CHRGVBUS (1UL << 15) #define TLL_CHANNEL_CONF_ULPINOBITSTUFF (1UL << 11) #define TLL_CHANNEL_CONF_ULPIAUTOIDLE (1UL << 10) #define TLL_CHANNEL_CONF_UTMIAUTOIDLE (1UL << 9) #define TLL_CHANNEL_CONF_ULPIDDRMODE (1UL << 8) #define TLL_CHANNEL_CONF_ULPIOUTCLKMODE (1UL << 7) #define TLL_CHANNEL_CONF_TLLFULLSPEED (1UL << 6) #define TLL_CHANNEL_CONF_TLLCONNECT (1UL << 5) #define TLL_CHANNEL_CONF_TLLATTACH (1UL << 4) #define TLL_CHANNEL_CONF_UTMIISADEV (1UL << 3) #define TLL_CHANNEL_CONF_CHANEN (1UL << 0) struct omap_tll_softc { device_t sc_dev; /* TLL register set */ struct resource* tll_mem_res; int tll_mem_rid; }; static struct omap_tll_softc *omap_tll_sc; static int omap_tll_attach(device_t dev); static int omap_tll_detach(device_t dev); static inline uint32_t omap_tll_read_4(struct omap_tll_softc *sc, bus_size_t off) { return bus_read_4(sc->tll_mem_res, off); } static inline void omap_tll_write_4(struct omap_tll_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->tll_mem_res, off, val); } void omap_tll_utmi_enable(unsigned int en_mask) { struct omap_tll_softc *sc; unsigned int i; uint32_t reg; sc = omap_tll_sc; if (sc == NULL) return; /* There are 3 TLL channels, one per USB controller so set them all up the * same, SDR mode, bit stuffing and no autoidle. */ for (i=0; i<3; i++) { reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE | TLL_CHANNEL_CONF_ULPINOBITSTUFF | TLL_CHANNEL_CONF_ULPIDDRMODE); omap_tll_write_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); } /* Program the common TLL register */ reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_SHARED_CONF); reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN | TLL_SHARED_CONF_USB_DIVRATIO_MASK); reg |= ( TLL_SHARED_CONF_FCLK_IS_ON | TLL_SHARED_CONF_USB_DIVRATIO_2 | TLL_SHARED_CONF_USB_180D_SDR_EN); omap_tll_write_4(sc, OMAP_USBTLL_TLL_SHARED_CONF, reg); /* Enable channels now */ for (i = 0; i < 3; i++) { reg = omap_tll_read_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i)); /* Enable only the reg that is needed */ if ((en_mask & (1 << i)) == 0) continue; reg |= TLL_CHANNEL_CONF_CHANEN; omap_tll_write_4(sc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg); } } static int omap_tll_init(struct omap_tll_softc *sc) { unsigned long timeout; int ret = 0; /* Enable the USB TLL */ - ti_prcm_clk_enable(USBTLL_CLK); + ti_sysc_clock_enable(device_get_parent(sc->sc_dev)); /* Perform TLL soft reset, and wait until reset is complete */ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); /* Set the timeout to 100ms*/ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); /* Wait for TLL reset to complete */ while ((omap_tll_read_4(sc, OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "TLL reset operation timed out\n"); ret = EINVAL; goto err_sys_status; } } /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq * assertion when no more activity on the USB. * ENAWAKEUP = 1 : Wakeup generation enabled */ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE | TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY); return(0); err_sys_status: /* Disable the TLL clocks */ - ti_prcm_clk_disable(USBTLL_CLK); + ti_sysc_clock_disable(device_get_parent(sc->sc_dev)); return(ret); } static void omap_tll_disable(struct omap_tll_softc *sc) { unsigned long timeout; timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); /* Reset the TLL module */ omap_tll_write_4(sc, OMAP_USBTLL_SYSCONFIG, 0x0002); while ((omap_tll_read_4(sc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "operation timed out\n"); break; } } /* Disable functional and interface clocks for the TLL and HOST modules */ - ti_prcm_clk_disable(USBTLL_CLK); + ti_sysc_clock_disable(device_get_parent(sc->sc_dev)); } static int omap_tll_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,usbhs-tll")) return (ENXIO); device_set_desc(dev, "TI OMAP USB 2.0 TLL module"); return (BUS_PROBE_DEFAULT); } static int omap_tll_attach(device_t dev) { struct omap_tll_softc *sc; sc = device_get_softc(dev); /* save the device */ sc->sc_dev = dev; /* Allocate resource for the TLL register set */ sc->tll_mem_rid = 0; sc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->tll_mem_rid, RF_ACTIVE); if (!sc->tll_mem_res) { device_printf(dev, "Error: Could not map TLL memory\n"); goto error; } omap_tll_init(sc); omap_tll_sc = sc; return (0); error: omap_tll_detach(dev); return (ENXIO); } static int omap_tll_detach(device_t dev) { struct omap_tll_softc *sc; sc = device_get_softc(dev); omap_tll_disable(sc); /* Release the other register set memory maps */ if (sc->tll_mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, sc->tll_mem_rid, sc->tll_mem_res); sc->tll_mem_res = NULL; } omap_tll_sc = NULL; return (0); } static device_method_t omap_tll_methods[] = { /* Device interface */ DEVMETHOD(device_probe, omap_tll_probe), DEVMETHOD(device_attach, omap_tll_attach), DEVMETHOD(device_detach, omap_tll_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), {0, 0} }; static driver_t omap_tll_driver = { "omap_tll", omap_tll_methods, sizeof(struct omap_tll_softc), }; static devclass_t omap_tll_devclass; DRIVER_MODULE(omap_tll, simplebus, omap_tll_driver, omap_tll_devclass, 0, 0); Index: head/sys/dev/uart/uart_dev_ti8250.c =================================================================== --- head/sys/dev/uart/uart_dev_ti8250.c (revision 363699) +++ head/sys/dev/uart/uart_dev_ti8250.c (revision 363700) @@ -1,138 +1,129 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Ian Lepore * 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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include -#include -#include - #include #include #include #include #include #include #include #include +#include + #include "uart_if.h" /* * High-level UART interface. */ struct ti8250_softc { struct ns8250_softc ns8250_base; /*uint32_t mystuff;*/ }; #define MDR1_REG 8 #define MDR1_MODE_UART 0 #define MDR1_MODE_DISABLE 7 #define SYSCC_REG 15 #define SYSCC_SOFTRESET (1 << 1) #define SYSS_REG 16 #define SYSS_STATUS_RESETDONE (1 << 0) static int ti8250_bus_probe(struct uart_softc *sc) { int status; - clk_ident_t clkid; - /* Enable clocks for this device. We can't continue if that fails. */ - clkid = ti_hwmods_get_clock(sc->sc_dev); - if (clkid == INVALID_CLK_IDENT) { - device_printf(sc->sc_dev, - "failed to get clock based on hwmods\n"); - clkid = UART1_CLK + device_get_unit(sc->sc_dev); - } - if ((status = ti_prcm_clk_enable(clkid)) != 0) + if ((status = ti_sysc_clock_enable(device_get_parent(sc->sc_dev))) != 0) return (status); /* * Set the hardware to disabled mode, do a full device reset, then set * it to uart mode. Most devices will be reset-and-disabled already, * but you never know what a bootloader might have done. */ uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_DISABLE); uart_setreg(&sc->sc_bas, SYSCC_REG, SYSCC_SOFTRESET); while (uart_getreg(&sc->sc_bas, SYSS_REG) & SYSS_STATUS_RESETDONE) continue; uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_UART); status = ns8250_bus_probe(sc); if (status == 0) device_set_desc(sc->sc_dev, "TI UART (16550 compatible)"); return (status); } static kobj_method_t ti8250_methods[] = { KOBJMETHOD(uart_probe, ti8250_bus_probe), KOBJMETHOD(uart_attach, ns8250_bus_attach), KOBJMETHOD(uart_detach, ns8250_bus_detach), KOBJMETHOD(uart_flush, ns8250_bus_flush), KOBJMETHOD(uart_getsig, ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, ns8250_bus_ipend), KOBJMETHOD(uart_param, ns8250_bus_param), KOBJMETHOD(uart_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD_END }; static struct uart_class uart_ti8250_class = { "ti8250", ti8250_methods, sizeof(struct ti8250_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 0x88, .uc_rclk = 48000000, .uc_rshift = 2 }; static struct ofw_compat_data compat_data[] = { {"ti,ns16550", (uintptr_t)&uart_ti8250_class}, {"ti,omap3-uart", (uintptr_t)&uart_ti8250_class}, {"ti,omap4-uart", (uintptr_t)&uart_ti8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/modules/Makefile =================================================================== --- head/sys/modules/Makefile (revision 363699) +++ head/sys/modules/Makefile (revision 363700) @@ -1,823 +1,823 @@ # $FreeBSD$ SYSDIR?=${SRCTOP}/sys .include "${SYSDIR}/conf/kern.opts.mk" SUBDIR_PARALLEL= # Modules that include binary-only blobs of microcode should be selectable by # MK_SOURCELESS_UCODE option (see below). .include "${SYSDIR}/conf/config.mk" .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) SUBDIR=${MODULES_OVERRIDE} .else SUBDIR= \ ${_3dfx} \ ${_3dfx_linux} \ ${_aac} \ ${_aacraid} \ accf_data \ accf_dns \ accf_http \ acl_nfs4 \ acl_posix1e \ ${_acpi} \ ae \ ${_aesni} \ age \ ${_agp} \ ahci \ aic7xxx \ alc \ ale \ alq \ ${_amd_ecc_inject} \ ${_amdgpio} \ ${_amdsbwd} \ ${_amdsmn} \ ${_amdtemp} \ amr \ ${_an} \ ${_aout} \ ${_apm} \ ${_arcmsr} \ ${_allwinner} \ ${_armv8crypto} \ ${_asmc} \ ata \ ath \ ath_dfs \ ath_hal \ ath_hal_ar5210 \ ath_hal_ar5211 \ ath_hal_ar5212 \ ath_hal_ar5416 \ ath_hal_ar9300 \ ath_main \ ath_rate \ ath_pci \ ${_autofs} \ ${_bce} \ ${_bcm283x_clkman} \ ${_bcm283x_pwm} \ bfe \ bge \ bhnd \ ${_bxe} \ ${_bios} \ ${_blake2} \ bnxt \ bridgestp \ bwi \ bwn \ ${_bytgpio} \ ${_chvgpio} \ cam \ ${_cardbus} \ ${_carp} \ cas \ ${_cbb} \ cc \ ${_ccp} \ cd9660 \ cd9660_iconv \ ${_ce} \ ${_cfi} \ ${_chromebook_platform} \ ${_ciss} \ cloudabi \ ${_cloudabi32} \ ${_cloudabi64} \ ${_cmx} \ ${_coretemp} \ ${_cp} \ ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ ${_cryptodev} \ ctl \ ${_cxgb} \ ${_cxgbe} \ dc \ dcons \ dcons_crom \ ${_dpms} \ dummynet \ ${_efirt} \ ${_em} \ ${_ena} \ esp \ ${_et} \ evdev \ ${_exca} \ ext2fs \ fdc \ fdescfs \ ${_ffec} \ filemon \ firewire \ firmware \ fusefs \ ${_fxp} \ gem \ geom \ ${_glxiic} \ ${_glxsb} \ gpio \ hifn \ hme \ ${_hpt27xx} \ ${_hptiop} \ ${_hptmv} \ ${_hptnr} \ ${_hptrr} \ hwpmc \ ${_hwpmc_mips24k} \ ${_hwpmc_mips74k} \ ${_hyperv} \ i2c \ ${_iavf} \ ${_ibcore} \ ${_ichwd} \ ${_ice} \ ${_ice_ddp} \ ${_ida} \ if_bridge \ if_disc \ if_edsc \ ${_if_enc} \ if_epair \ ${_if_gif} \ ${_if_gre} \ ${_if_me} \ if_lagg \ ${_if_ndis} \ ${_if_stf} \ if_tuntap \ if_vlan \ if_vxlan \ iflib \ ${_iir} \ imgact_binmisc \ ${_intelspi} \ ${_io} \ ${_ioat} \ ${_ipoib} \ ${_ipdivert} \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ ${_ipfw_nat64} \ ${_ipfw_nptv6} \ ${_ipfw_pmod} \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ ${_ips} \ ${_ipsec} \ ${_ipw} \ ${_ipwfw} \ ${_isci} \ ${_iser} \ isp \ ${_ispfw} \ ${_itwd} \ ${_iwi} \ ${_iwifw} \ ${_iwm} \ ${_iwmfw} \ ${_iwn} \ ${_iwnfw} \ ${_ix} \ ${_ixv} \ ${_ixl} \ jme \ kbdmux \ kgssapi \ kgssapi_krb5 \ khelp \ krpc \ ksyms \ ${_ktls_ocf} \ le \ lge \ libalias \ libiconv \ libmchain \ lindebugfs \ linuxkpi \ ${_lio} \ lpt \ mac_biba \ mac_bsdextended \ mac_ifoff \ mac_lomac \ mac_mls \ mac_none \ mac_ntpd \ mac_partition \ mac_portacl \ mac_seeotheruids \ mac_stub \ mac_test \ malo \ md \ mdio \ mem \ mfi \ mii \ mlx \ mlxfw \ ${_mlx4} \ ${_mlx4ib} \ ${_mlx4en} \ ${_mlx5} \ ${_mlx5en} \ ${_mlx5ib} \ ${_mly} \ mmc \ mmcsd \ ${_mpr} \ ${_mps} \ mpt \ mqueue \ mrsas \ msdosfs \ msdosfs_iconv \ msk \ ${_mthca} \ mvs \ mwl \ ${_mwlfw} \ mxge \ my \ ${_nctgpio} \ ${_ndis} \ ${_netgraph} \ ${_nfe} \ nfscl \ nfscommon \ nfsd \ nfslockd \ nfssvc \ nge \ nmdm \ nullfs \ ${_ntb} \ ${_nvd} \ ${_nvdimm} \ ${_nvme} \ ${_nvram} \ oce \ ${_ocs_fc} \ otus \ ${_otusfw} \ ow \ ${_padlock} \ ${_padlock_rng} \ ${_pccard} \ ${_pchtherm} \ ${_pcfclock} \ ${_pf} \ ${_pflog} \ ${_pfsync} \ plip \ ${_pms} \ ppbus \ ppc \ ppi \ pps \ procfs \ proto \ pseudofs \ ${_pst} \ pty \ puc \ pwm \ ${_qlxge} \ ${_qlxgb} \ ${_qlxgbe} \ ${_qlnx} \ ral \ ${_ralfw} \ ${_random_fortuna} \ ${_random_other} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ re \ rl \ ${_rockchip} \ rtwn \ rtwn_pci \ rtwn_usb \ ${_rtwnfw} \ ${_s3} \ ${_safe} \ safexcel \ ${_sbni} \ scc \ ${_sctp} \ sdhci \ ${_sdhci_acpi} \ sdhci_pci \ sdio \ sem \ send \ ${_sfxge} \ sge \ ${_sgx} \ ${_sgx_linux} \ siftr \ siis \ sis \ sk \ ${_smartpqi} \ smbfs \ snp \ sound \ ${_speaker} \ spi \ ${_splash} \ ${_sppp} \ ste \ stge \ ${_superio} \ ${_sym} \ ${_syscons} \ sysvipc \ tcp \ ${_ti} \ tmpfs \ ${_toecore} \ ${_tpm} \ ${_twa} \ twe \ tws \ uart \ udf \ udf_iconv \ ufs \ uinput \ unionfs \ usb \ ${_vesa} \ ${_virtio} \ vge \ ${_viawd} \ videomode \ vkbd \ ${_vmd} \ ${_vmm} \ ${_vmware} \ vr \ vte \ ${_wbwd} \ ${_wi} \ wlan \ wlan_acl \ wlan_amrr \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ ${_wpi} \ ${_wpifw} \ ${_x86bios} \ xdr \ xl \ xz \ zlib .if ${MK_AUTOFS} != "no" || defined(ALL_MODULES) _autofs= autofs .endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) .if (${MACHINE_CPUARCH} != "arm" || ${MACHINE_ARCH:Marmv[67]*} != "") && \ ${MACHINE_CPUARCH} != "mips" .if ${KERN_OPTS:MKDTRACE_HOOKS} SUBDIR+= dtrace .endif .endif SUBDIR+= opensolaris .endif .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${SRCTOP}/sys/opencrypto) _crypto= crypto _cryptodev= cryptodev _random_fortuna=random_fortuna _random_other= random_other _ktls_ocf= ktls_ocf .endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) SUBDIR+= cuse .endif .if ${MK_EFI} != "no" .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _efirt= efirt .endif .endif .if (${MK_INET_SUPPORT} != "no" || ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _carp= carp _toecore= toecore _if_enc= if_enc _if_gif= if_gif _if_gre= if_gre _ipfw_pmod= ipfw_pmod .if ${KERN_OPTS:MIPSEC_SUPPORT} && !${KERN_OPTS:MIPSEC} _ipsec= ipsec .endif .if ${KERN_OPTS:MSCTP_SUPPORT} || ${KERN_OPTS:MSCTP} _sctp= sctp .endif .endif .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _if_stf= if_stf .endif .if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES) _if_me= if_me _ipdivert= ipdivert _ipfw= ipfw .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nat64= ipfw_nat64 .endif .endif .if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES) _ipfw_nptv6= ipfw_nptv6 .endif .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif .if ${MK_ISCSI} != "no" || defined(ALL_MODULES) SUBDIR+= cfiscsi SUBDIR+= iscsi SUBDIR+= iscsi_initiator .endif .if !empty(OPT_FDT) SUBDIR+= fdt .endif # Linuxulator .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" SUBDIR+= linprocfs SUBDIR+= linsysfs .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SUBDIR+= linux .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" SUBDIR+= linux64 SUBDIR+= linux_common .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" _ena= ena .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ibcore= ibcore _ipoib= ipoib _iser= iser .endif _mlx4= mlx4 _mlx5= mlx5 .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _mlx4en= mlx4en _mlx5en= mlx5en .endif .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mthca= mthca _mlx4ib= mlx4ib _mlx5ib= mlx5ib .endif .endif .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif .if (${MK_PF} != "no" && (${MK_INET_SUPPORT} != "no" || \ ${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES) _pf= pf _pflog= pflog .if ${MK_INET_SUPPORT} != "no" _pfsync= pfsync .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" _bce= bce _fxp= fxp _ispfw= ispfw _ti= ti .if ${MACHINE_CPUARCH} != "mips" _mwlfw= mwlfw _otusfw= otusfw _ralfw= ralfw _rtwnfw= rtwnfw .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "riscv" _cxgbe= cxgbe .endif .if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "arm64" _ice= ice .if ${MK_SOURCELESS_UCODE} != "no" _ice_ddp= ice_ddp .endif .endif # These rely on 64bit atomics .if ${MACHINE_ARCH} != "powerpc" && ${MACHINE_ARCH} != "powerpcspe" && \ ${MACHINE_CPUARCH} != "mips" _mps= mps _mpr= mpr .endif .if ${MK_TESTS} != "no" || defined(ALL_MODULES) SUBDIR+= tests .endif .if ${MK_ZFS} != "no" || defined(ALL_MODULES) SUBDIR+= zfs .endif .if (${MACHINE_CPUARCH} == "mips" && ${MACHINE_ARCH:Mmips64} == "") _hwpmc_mips24k= hwpmc_mips24k _hwpmc_mips74k= hwpmc_mips74k .endif .if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && ${MACHINE_CPUARCH} != "powerpc" && \ ${MACHINE_CPUARCH} != "riscv" _syscons= syscons .endif .if ${MACHINE_CPUARCH} != "mips" # no BUS_SPACE_UNSPECIFIED # No barrier instruction support (specific to this driver) _sym= sym # intr_disable() is a macro, causes problems .if ${MK_SOURCELESS_UCODE} != "no" _cxgb= cxgb .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" _allwinner= allwinner _armv8crypto= armv8crypto _em= em _rockchip= rockchip .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _agp= agp _an= an _aout= aout _bios= bios .if ${MK_SOURCELESS_UCODE} != "no" _bxe= bxe .endif _cardbus= cardbus _cbb= cbb _cpuctl= cpuctl _cpufreq= cpufreq _dpms= dpms _em= em _et= et _exca= exca _if_ndis= if_ndis _io= io _itwd= itwd _ix= ix _ixv= ixv .if ${MK_SOURCELESS_UCODE} != "no" _lio= lio .endif _nctgpio= nctgpio _ndis= ndis _ntb= ntb _ocs_fc= ocs_fc _pccard= pccard .if ${MK_OFED} != "no" || defined(ALL_MODULES) _rdma= rdma .endif _safe= safe _speaker= speaker _splash= splash _sppp= sppp _vmware= vmware _wbwd= wbwd _wi= wi _aac= aac _aacraid= aacraid _acpi= acpi .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _aesni= aesni .endif _amd_ecc_inject=amd_ecc_inject _amdsbwd= amdsbwd _amdsmn= amdsmn _amdtemp= amdtemp _arcmsr= arcmsr _asmc= asmc .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _blake2= blake2 .endif _bytgpio= bytgpio _chvgpio= chvgpio _ciss= ciss _chromebook_platform= chromebook_platform _cmx= cmx _coretemp= coretemp .if ${MK_SOURCELESS_HOST} != "no" && empty(KCSAN_ENABLED) _hpt27xx= hpt27xx .endif _hptiop= hptiop .if ${MK_SOURCELESS_HOST} != "no" && empty(KCSAN_ENABLED) _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif _hyperv= hyperv _ichwd= ichwd _ida= ida _iir= iir _intelspi= intelspi _ipmi= ipmi _ips= ips _isci= isci _ipw= ipw _iwi= iwi _iwm= iwm _iwn= iwn .if ${MK_SOURCELESS_UCODE} != "no" _ipwfw= ipwfw _iwifw= iwifw _iwmfw= iwmfw _iwnfw= iwnfw .endif _mly= mly _nfe= nfe _nvd= nvd _nvme= nvme _nvram= nvram .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock _padlock_rng= padlock_rng _rdrand_rng= rdrand_rng .endif _pchtherm = pchtherm _s3= s3 _sdhci_acpi= sdhci_acpi _superio= superio _tpm= tpm _twa= twa _vesa= vesa _viawd= viawd _virtio= virtio _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" _wpifw= wpifw .endif _x86bios= x86bios .endif .if ${MACHINE_CPUARCH} == "amd64" _amdgpio= amdgpio _ccp= ccp _iavf= iavf _ioat= ioat _ixl= ixl _nvdimm= nvdimm _pms= pms _qlxge= qlxge _qlxgb= qlxgb _vmd= vmd .if ${MK_SOURCELESS_UCODE} != "no" _qlxgbe= qlxgbe _qlnx= qlnx .endif _sfxge= sfxge _sgx= sgx _sgx_linux= sgx_linux _smartpqi= smartpqi .if ${MK_BHYVE} != "no" || defined(ALL_MODULES) .if ${KERN_OPTS:MSMP} _vmm= vmm .endif .endif .endif .if ${MACHINE_CPUARCH} == "i386" # XXX some of these can move to the general case when de-i386'ed # XXX some of these can move now, but are untested on other architectures. _3dfx= 3dfx _3dfx_linux= 3dfx_linux _apm= apm .if ${MK_SOURCELESS_HOST} != "no" _ce= ce .endif .if ${MK_SOURCELESS_UCODE} != "no" _cp= cp .endif _glxiic= glxiic _glxsb= glxsb _pcfclock= pcfclock _pst= pst _sbni= sbni .endif -.if ${MACHINE_CPUARCH} == "arm" +.if ${MACHINE_ARCH} == "armv7" _cfi= cfi _cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "powerpc" _aacraid= aacraid _agp= agp _an= an _cardbus= cardbus _cbb= cbb _cfi= cfi _cpufreq= cpufreq _exca= exca _ffec= ffec _nvd= nvd _nvme= nvme _pccard= pccard _wi= wi _virtio= virtio .endif .if ${MACHINE_ARCH} == "powerpc64" _ipmi= ipmi _ixl= ixl _nvram= opal_nvram .endif .if ${MACHINE_ARCH} == "powerpc64" || ${MACHINE_ARCH} == "powerpc" # Don't build powermac_nvram for powerpcspe, it's never supported. _nvram+= powermac_nvram .endif .if (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "i386") _cloudabi32= cloudabi32 .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _cloudabi64= cloudabi64 .endif .endif .if ${MACHINE_ARCH:Marmv[67]*} != "" || ${MACHINE_CPUARCH} == "aarch64" _bcm283x_clkman= bcm283x_clkman _bcm283x_pwm= bcm283x_pwm .endif SUBDIR+=${MODULES_EXTRA} .for reject in ${WITHOUT_MODULES} SUBDIR:= ${SUBDIR:N${reject}} .endfor # Calling kldxref(8) for each module is expensive. .if !defined(NO_XREF) .MAKEFLAGS+= -DNO_XREF afterinstall: .PHONY @if type kldxref >/dev/null 2>&1; then \ ${ECHO} ${KLDXREF_CMD} ${DESTDIR}${KMODDIR}; \ ${KLDXREF_CMD} ${DESTDIR}${KMODDIR}; \ fi .endif SUBDIR:= ${SUBDIR:u:O} .include