From cf3c20aec4364be4014452cd0299590e4160e822 Mon Sep 17 00:00:00 2001 From: kremlin Date: Wed, 4 May 2016 00:49:38 -0400 Subject: [PATCH] initial commit, pull in sys/arch/armv7/omap --- am335x.c | 264 ++++++++ am335x_cm_padconf.c | 350 +++++++++++ am335x_prcmreg.h | 57 ++ dmtimer.c | 413 +++++++++++++ edma.c | 283 +++++++++ edmavar.h | 49 ++ files.omap | 92 +++ gptimer.c | 441 ++++++++++++++ if_cpsw.c | 1254 ++++++++++++++++++++++++++++++++++++++ if_cpswreg.h | 146 +++++ intc.c | 423 +++++++++++++ intc.h | 74 +++ omap.c | 189 ++++++ omap3.c | 196 ++++++ omap3_prcmreg.h | 222 +++++++ omap4.c | 200 +++++++ omap4_prcmreg.h | 27 + omap_com.c | 112 ++++ omap_machdep.c | 137 +++++ omapid.c | 101 ++++ omdisplay.c | 1394 +++++++++++++++++++++++++++++++++++++++++++ omdog.c | 190 ++++++ omehci.c | 486 +++++++++++++++ omehcivar.h | 232 +++++++ omgpio.c | 763 +++++++++++++++++++++++ omgpiovar.h | 44 ++ ommmc.c | 1153 +++++++++++++++++++++++++++++++++++ omohci.c | 351 +++++++++++ omusbtll.c | 175 ++++++ prcm.c | 542 +++++++++++++++++ prcmvar.h | 57 ++ sitara_cm.c | 394 ++++++++++++ sitara_cm.h | 78 +++ sitara_cmreg.h | 46 ++ ti_iic.c | 589 ++++++++++++++++++ ti_iicreg.h | 136 +++++ 36 files changed, 11660 insertions(+) create mode 100644 am335x.c create mode 100644 am335x_cm_padconf.c create mode 100644 am335x_prcmreg.h create mode 100644 dmtimer.c create mode 100644 edma.c create mode 100644 edmavar.h create mode 100644 files.omap create mode 100644 gptimer.c create mode 100644 if_cpsw.c create mode 100644 if_cpswreg.h create mode 100644 intc.c create mode 100644 intc.h create mode 100644 omap.c create mode 100644 omap3.c create mode 100644 omap3_prcmreg.h create mode 100644 omap4.c create mode 100644 omap4_prcmreg.h create mode 100644 omap_com.c create mode 100644 omap_machdep.c create mode 100644 omapid.c create mode 100644 omdisplay.c create mode 100644 omdog.c create mode 100644 omehci.c create mode 100644 omehcivar.h create mode 100644 omgpio.c create mode 100644 omgpiovar.h create mode 100644 ommmc.c create mode 100644 omohci.c create mode 100644 omusbtll.c create mode 100644 prcm.c create mode 100644 prcmvar.h create mode 100644 sitara_cm.c create mode 100644 sitara_cm.h create mode 100644 sitara_cmreg.h create mode 100644 ti_iic.c create mode 100644 ti_iicreg.h diff --git a/am335x.c b/am335x.c new file mode 100644 index 0000000..05fe86e --- /dev/null +++ b/am335x.c @@ -0,0 +1,264 @@ +/* $OpenBSD: am335x.c,v 1.7 2014/03/18 07:34:17 syl Exp $ */ + +/* + * Copyright (c) 2011 Uwe Stuehler + * Copyright (c) 2013 Raphael Graf + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include + +#define PRCM_SIZE 0x2000 +#define PRCM_ADDR 0x44E00000 + +#define SCM_SIZE 0x2000 +#define SCM_ADDR 0x44E10000 + +#define INTC_SIZE 0x300 +#define INTC_ADDR 0x48200000 + +#define DMTIMERx_SIZE 0x80 +#define DMTIMER0_ADDR 0x44E05000 +#define DMTIMER1_ADDR 0x44E31000 /* 1MS */ +#define DMTIMER2_ADDR 0x48040000 +#define DMTIMER3_ADDR 0x48042000 +#define DMTIMER4_ADDR 0x48044000 +#define DMTIMER5_ADDR 0x48046000 +#define DMTIMER6_ADDR 0x48048000 +#define DMTIMER7_ADDR 0x4804A000 +#define DMTIMER0_IRQ 66 +#define DMTIMER1_IRQ 67 +#define DMTIMER2_IRQ 68 +#define DMTIMER3_IRQ 69 +#define DMTIMER4_IRQ 92 +#define DMTIMER5_IRQ 93 +#define DMTIMER6_IRQ 94 +#define DMTIMER7_IRQ 95 + +#define WD_SIZE 0x80 +#define WD_ADDR 0x44E35000 +#define WD_IRQ 91 + +#define GPIOx_SIZE 0x200 +#define GPIO0_ADDR 0x44E07000 +#define GPIO1_ADDR 0x4804C000 +#define GPIO2_ADDR 0x481AC000 +#define GPIO3_ADDR 0x481AE000 +#define GPIO0_IRQ 96 +#define GPIO1_IRQ 98 +#define GPIO2_IRQ 32 +#define GPIO3_IRQ 62 + +#define TPCC_SIZE 0x100000 +#define TPCC_ADDR 0x49000000 +#define TPTC0_ADDR 0x49800000 +#define TPTC1_ADDR 0x49900000 +#define TPTC2_ADDR 0x49a00000 +#define EDMACOMP_IRQ 12 +#define EDMAMPERR_IRQ 13 +#define EDMAERR_IRQ 14 + +#define UARTx_SIZE 0x90 +#define UART0_ADDR 0x44E09000 +#define UART1_ADDR 0x48022000 +#define UART2_ADDR 0x48024000 +#define UART3_ADDR 0x481A6000 +#define UART4_ADDR 0x481A8000 +#define UART5_ADDR 0x481AA000 +#define UART0_IRQ 72 +#define UART1_IRQ 73 +#define UART2_IRQ 74 +#define UART3_IRQ 44 +#define UART4_IRQ 45 +#define UART5_IRQ 46 + +#define HSMMCx_SIZE 0x200 +#define HSMMC0_ADDR 0x48060100 +#define HSMMC1_ADDR 0x481d8100 +#define HSMMC0_IRQ 64 +#define HSMMC1_IRQ 28 + +#define CPSW_SIZE 0x4000 +#define CPSW_ADDR 0x4A100000 +#define CPSW_IRQ 40 + +#define IICx_SIZE 0x1000 +#define IIC0_ADDR 0x44e0b000 +#define IIC1_ADDR 0x4802a000 +#define IIC2_ADDR 0x4819c000 +#define IIC0_IRQ 70 +#define IIC1_IRQ 71 +#define IIC2_IRQ 30 + +struct armv7_dev am335x_devs[] = { + + /* + * Power, Reset and Clock Manager + */ + + { .name = "prcm", + .unit = 0, + .mem = { { PRCM_ADDR, PRCM_SIZE } }, + }, + + /* + * System Control Module + */ + + { .name = "sitaracm", + .unit = 0, + .mem = { { SCM_ADDR, SCM_SIZE } }, + }, + + /* + * Interrupt Controller + */ + + { .name = "intc", + .unit = 0, + .mem = { { INTC_ADDR, INTC_SIZE } }, + }, + + /* + * EDMA Controller + */ + { .name = "edma", + .unit = 0, + .mem = { { TPCC_ADDR, TPCC_SIZE } }, + .irq = { EDMACOMP_IRQ } + }, + + /* + * General Purpose Timers + */ + + { .name = "dmtimer", + .unit = 0, + .mem = { { DMTIMER2_ADDR, DMTIMERx_SIZE } }, + .irq = { DMTIMER2_IRQ } + }, + + { .name = "dmtimer", + .unit = 1, + .mem = { { DMTIMER3_ADDR, DMTIMERx_SIZE } }, + .irq = { DMTIMER3_IRQ } + }, + + /* + * Watchdog Timer + */ + + { .name = "omdog", + .unit = 0, + .mem = { { WD_ADDR, WD_SIZE } } + }, + + /* + * UART + */ + + { .name = "com", + .unit = 0, + .mem = { { UART0_ADDR, UARTx_SIZE } }, + .irq = { UART0_IRQ } + }, + + /* + * GPIO + */ + + { .name = "omgpio", + .unit = 0, + .mem = { { GPIO0_ADDR, GPIOx_SIZE } }, + .irq = { GPIO0_IRQ } + }, + + { .name = "omgpio", + .unit = 1, + .mem = { { GPIO1_ADDR, GPIOx_SIZE } }, + .irq = { GPIO1_IRQ } + }, + + { .name = "omgpio", + .unit = 2, + .mem = { { GPIO2_ADDR, GPIOx_SIZE } }, + .irq = { GPIO2_IRQ } + }, + + { .name = "omgpio", + .unit = 3, + .mem = { { GPIO3_ADDR, GPIOx_SIZE } }, + .irq = { GPIO3_IRQ } + }, + + /* + * IIC + */ + + { .name = "tiiic", + .unit = 0, + .mem = { { IIC0_ADDR, IICx_SIZE } }, + .irq = { IIC0_IRQ } + }, + + { .name = "tiiic", + .unit = 1, + .mem = { { IIC1_ADDR, IICx_SIZE } }, + .irq = { IIC1_IRQ } + }, + + { .name = "tiiic", + .unit = 2, + .mem = { { IIC2_ADDR, IICx_SIZE } }, + .irq = { IIC2_IRQ } + }, + + /* + * MMC + */ + + { .name = "ommmc", + .unit = 0, + .mem = { { HSMMC0_ADDR, HSMMCx_SIZE } }, + .irq = { HSMMC0_IRQ } + }, + + { .name = "ommmc", + .unit = 1, + .mem = { { HSMMC1_ADDR, HSMMCx_SIZE } }, + .irq = { HSMMC1_IRQ } + }, + + /* cpsw Ethernet */ + { .name = "cpsw", + .unit = 0, + .mem = { { CPSW_ADDR, CPSW_SIZE } }, + .irq = { CPSW_IRQ } + }, + + /* Terminator */ + { .name = NULL, + .unit = 0 + } +}; + +void +am335x_init(void) +{ + armv7_set_devs(am335x_devs); +} diff --git a/am335x_cm_padconf.c b/am335x_cm_padconf.c new file mode 100644 index 0000000..3723b99 --- /dev/null +++ b/am335x_cm_padconf.c @@ -0,0 +1,350 @@ +/* $OpenBSD: am335x_cm_padconf.c,v 1.1 2013/09/04 14:38:30 patrick Exp $ */ +/* $NetBSD: am335x_cm_padconf.c,v 1.2 2013/05/06 18:53:40 rkujawa Exp $ */ +/*- + * 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 + +#include +#include +#include +#include +#include + +#define _PIN(r, b, gp, gm, m0, m1, m2, m3, m4, m5, m6, m7) \ + { .reg_off = r, \ + .gpio_pin = gp, \ + .gpio_mode = gm, \ + .ballname = b, \ + .muxmodes[0] = m0, \ + .muxmodes[1] = m1, \ + .muxmodes[2] = m2, \ + .muxmodes[3] = m3, \ + .muxmodes[4] = m4, \ + .muxmodes[5] = m5, \ + .muxmodes[6] = m6, \ + .muxmodes[7] = m7, \ + } + +#define SLEWCTRL (0x01 << 6) /* faster(0) or slower(1) slew rate. */ +#define RXACTIVE (0x01 << 5) /* Input enable value for the Pad */ +#define PULLTYPESEL (0x01 << 4) /* Pad pullup/pulldown type selection */ +#define PULLUDEN (0x01 << 3) /* Pullup/pulldown disabled */ + +#define PADCONF_OUTPUT (0) +#define PADCONF_OUTPUT_PULLUP (PULLTYPESEL) +#define PADCONF_INPUT (RXACTIVE | PULLUDEN) +#define PADCONF_INPUT_PULLUP (RXACTIVE | PULLTYPESEL) +#define PADCONF_INPUT_PULLDOWN (RXACTIVE) +#define PADCONF_INPUT_PULLUP_SLOW (PADCONF_INPUT_PULLUP | SLEWCTRL) + +const struct sitara_cm_padstate ti_padstate_devmap[] = { + {"output", PADCONF_OUTPUT }, + {"output_pullup", PADCONF_OUTPUT_PULLUP }, + {"input", PADCONF_INPUT }, + {"input_pulldown", PADCONF_INPUT_PULLDOWN }, + {"input_pullup", PADCONF_INPUT_PULLUP }, + {"i2c", PADCONF_INPUT_PULLUP_SLOW }, + { .state = NULL } +}; + +const struct sitara_cm_padconf ti_padconf_devmap[] = { + _PIN(0x800, "GPMC_AD0", 32, 7,"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"), + _PIN(0x804, "GPMC_AD1", 33, 7,"gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"), + _PIN(0x808, "GPMC_AD2", 34, 7,"gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"), + _PIN(0x80C, "GPMC_AD3", 35, 7,"gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"), + _PIN(0x810, "GPMC_AD4", 36, 7,"gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"), + _PIN(0x814, "GPMC_AD5", 37, 7,"gpmc_ad5", "mmc1_dat5", NULL, NULL, NULL, NULL, NULL, "gpio1_5"), + _PIN(0x818, "GPMC_AD6", 38, 7,"gpmc_ad6", "mmc1_dat6", NULL, NULL, NULL, NULL, NULL, "gpio1_6"), + _PIN(0x81C, "GPMC_AD7", 39, 7,"gpmc_ad7", "mmc1_dat7", NULL, NULL, NULL, NULL, NULL, "gpio1_7"), + _PIN(0x820, "GPMC_AD8", 22, 7, "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4", "ehrpwm2A", NULL, NULL, "gpio0_22"), + _PIN(0x824, "GPMC_AD9", 23, 7, "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5", "ehrpwm2B", NULL, NULL, "gpio0_23"), + _PIN(0x828, "GPMC_AD10", 26, 7, "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6", "ehrpwm2_tripzone_in", NULL, NULL, "gpio0_26"), + _PIN(0x82C, "GPMC_AD11", 27, 7, "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7", "ehrpwm0_synco", NULL, NULL, "gpio0_27"), + _PIN(0x830, "GPMC_AD12", 44, 7, "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0", "eQEP2A_in", "pr1_mii0_txd2", "pr1_pru0_pru_r30_14", "gpio1_12"), + _PIN(0x834, "GPMC_AD13", 45, 7, "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1", "eQEP2B_in", "pr1_mii0_txd1", "pr1_pru0_pru_r30_15", "gpio1_13"), + _PIN(0x838, "GPMC_AD14", 46, 7, "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2", "eQEP2_index", "pr1_mii0_txd0", "pr1_pru0_pru_r31_14", "gpio1_14"), + _PIN(0x83C, "GPMC_AD15", 47, 7, "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3", "eQEP2_strobe", "pr1_ecap0_ecap_capin_apwm_o", "pr1_pru0_pru_r31_15", "gpio1_15"), + _PIN(0x840, "GPMC_A0", 48, 7, "gpmc_a0", "gmii2_txen", "rgmii2_tctl", "rmii2_txen", "gpmc_a16", "pr1_mii_mt1_clk", "ehrpwm1_tripzone_input", "gpio1_16"), + _PIN(0x844, "GPMC_A1", 49, 7, "gpmc_a1", "gmii2_rxdv", "rgmii2_rctl", "mmc2_dat0", "gpmc_a17", "pr1_mii1_txd3", "ehrpwm0_synco", "gpio1_17"), + _PIN(0x848, "GPMC_A2", 50, 7, "gpmc_a2", "gmii2_txd3", "rgmii2_td3", "mmc2_dat1", "gpmc_a18", "pr1_mii1_txd2", "ehrpwm1A", "gpio1_18"), + _PIN(0x84C, "GPMC_A3", 51, 7, "gpmc_a3", "gmii2_txd2", "rgmii2_td2", "mmc2_dat2", "gpmc_a19", "pr1_mii1_txd1", "ehrpwm1B", "gpio1_19"), + _PIN(0x850, "GPMC_A4", 52, 7, "gpmc_a4", "gmii2_txd1", "rgmii2_td1", "rmii2_tdx1", "gpmc_a20", "pr1_mii1_txd0", "eQEP1A_in", "gpio1_20"), + _PIN(0x854, "GPMC_A5", 53, 7, "gpmc_a5", "gmii2_txd0", "rgmii2_td0", "rmii2_txd0", "gpmc_a21", "pr1_mii1_rxd3", "eQEP1B_in", "gpio1_21"), + _PIN(0x858, "GPMC_A6", 54, 7, "gpmc_a6", "gmii2_txclk", "rgmii2_tclk", "mmc2_dat4", "gpmc_a22", "pr1_mii1_rxd2", "eQEP1_index", "gpio1_22"), + _PIN(0x85C, "GPMC_A7", 55, 7, "gpmc_a7", "gmii2_rxclk", "rgmii2_rclk", "mmc2_dat5", "gpmc_a23", "pr1_mii1_rxd1", "eQEP1_strobe", "gpio1_23"), + _PIN(0x860, "GPMC_A8", 56, 7, "gpmc_a8", "gmii2_rxd3", "rgmii2_rd3", "mmc2_dat6", "gpmc_a24", "pr1_mii1_rxd0", "mcasp0_aclkx", "gpio1_24"), + _PIN(0x864, "GPMC_A9", 57, 7, "gmpc_a9", "gmii2_rxd2", "rgmii2_rd2", "mmc2_dat7 / rmii2_crs_dv", "gpmc_a25", "pr1_mii_mr1_clk", "mcasp0_fsx", "gpio1_25"), + _PIN(0x868, "GPMC_A10", 58, 7, "gmpc_a10", "gmii2_rxd1", "rgmii2_rd1", "rmii2_rxd1", "gpmc_a26", "pr1_mii1_rxdv", "mcasp0_arx0", "gpio1_26"), + _PIN(0x86C, "GPMC_A11", 59, 7, "gmpc_a11", "gmii2_rxd0", "rgmii2_rd0", "rmii2_rxd0", "gpmc_a27", "pr1_mii1_rxer", "mcasp0_axr1", "gpio1_27"), + _PIN(0x870, "GPMC_WAIT0", 30, 7, "gpmc_wait0", "gmii2_crs", "gpmc_csn4", "rmii2_crs_dv", "mmc1_sdcd", "pr1_mii1_col", "uart4_rxd", "gpio0_30"), + _PIN(0x874, "GPMC_WPn", 31, 7, "gpmc_wpn", "gmii2_rxerr", "gpmc_csn5", "rmii2_rxerr", "mmc2_sdcd", "pr1_mii1_txen", "uart4_txd", "gpio0_31"), + _PIN(0x878, "GPMC_BEn1", 60, 7, "gpmc_be1n", "gmii2_col", "gmpc_csn6","mmc2_dat3", "gpmc_dir", "pr1_mii1_rxlink", "mcasp0_aclkr", "gpio1_28"), + _PIN(0x87c, "GPMC_CSn0", 61, 7, "gpmc_csn0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio1_29"), + _PIN(0x880, "GPMC_CSn1", 62, 7, "gpmc_csn1", "gpmc_clk", "mmc1_clk", "pr1_edio_data_in6", "pr1_edio_data_out6", "pr1_pru1_pru_r30_12", "pr1_pru1_pru_r31_12", "gpio1_30"), + _PIN(0x884, "GPMC_CSn2", 63, 7, "gpmc_csn2", "gpmc_be1n", "mmc1_cmd", "pr1_edio_data_in7", "pr1_edio_data_out7", "pr1_pru1_pru_r30_13", "pr1_pru1_pru_r31_13", "gpio1_31"), + _PIN(0x888, "GPMC_CSn3", 64, 7, "gpmc_csn3", "gpmc_a3", "rmii2_crs_dv", "mmc2_cmd", "pr1_mii0_crs", "pr1_mdio_data", "EMU4", "gpio2_0"), + _PIN(0x88c, "GPMC_CLK", 65, 7, "gpmc_clk", "lcd_memory_clk", "gpmc_wait1", "mmc2_clk", "pr1_mii1_crs", "pr1_mdio_mdclk", "mcasp0_fsr", "gpio2_1"), + _PIN(0x890, "GPMC_ADVn_ALE", 66, 7, "gpmc_advn_ale", NULL, "timer4", NULL, NULL, NULL, NULL, "gpio2_2"), + _PIN(0x894, "GPMC_OEn_REn", 67, 7, "gpmc_oen_ren", NULL, "timer7", NULL, NULL, NULL, NULL, "gpio2_3"), + _PIN(0x898, "GPMC_WEn", 68, 7, "gpmc_wen", NULL, "timer6", NULL, NULL, NULL, NULL, "gpio2_4"), + _PIN(0x89c, "GPMC_BEn0_CLE", 67, 7, "gpmc_ben0_cle", NULL, "timer5", NULL, NULL, NULL, NULL, "gpio2_5"), + _PIN(0x8a0, "LCD_DATA0", 68, 7, "lcd_data0", "gpmc_a0", "pr1_mii_mt0_clk", "ehrpwm2A", NULL, "pr1_pru1_pru_r30_0", "pr1_pru1_pru_r31_0", "gpio2_6"), + _PIN(0x8a4, "LCD_DATA1", 69, 7, "lcd_data1", "gpmc_a1", "pr1_mii0_txen", "ehrpwm2B", NULL, "pr1_pru1_pru_r30_1", "pr1_pru1_pru_r31_1", "gpio2_7"), + _PIN(0x8a8, "LCD_DATA2", 70, 7, "lcd_data2", "gpmc_a2", "pr1_mii0_txd3", "ehrpwm2_tripzone_input", NULL, "pr1_pru1_pru_r30_2", "pr1_pru1_pru_r31_2", "gpio2_8"), + _PIN(0x8ac, "LCD_DATA3", 71, 7, "lcd_data3", "gpmc_a3", "pr1_mii0_txd2", "ehrpwm0_synco", NULL, "pr1_pru1_pru_r30_3", "pr1_pru1_pru_r31_3", "gpio2_9"), + _PIN(0x8b0, "LCD_DATA4", 72, 7, "lcd_data4", "gpmc_a4", "pr1_mii0_txd1", "eQEP2A_in", NULL, "pr1_pru1_pru_r30_4", "pr1_pru1_pru_r31_4", "gpio2_10"), + _PIN(0x8b4, "LCD_DATA5", 73, 7, "lcd_data5", "gpmc_a5", "pr1_mii0_txd0", "eQEP2B_in", NULL, "pr1_pru1_pru_r30_5", "pr1_pru1_pru_r31_5", "gpio2_11"), + _PIN(0x8b8, "LCD_DATA6", 74, 7, "lcd_data6", "gpmc_a6", "pr1_edio_data_in6", "eQEP2_index", "pr1_edio_data_out6", "pr1_pru1_pru_r30_6", "pr1_pru1_pru_r31_6", "gpio2_12"), + _PIN(0x8bc, "LCD_DATA7", 75, 7, "lcd_data7", "gpmc_a7", "pr1_edio_data_in7", "eQEP2_strobe", "pr1_edio_data_out7", "pr1_pru1_pru_r30_7", "pr1_pru1_pru_r31_7", "gpio2_13"), + _PIN(0x8c0, "LCD_DATA8", 76, 7, "lcd_data8", "gpmc_a12", "ehrpwm1_tripzone_input", "mcasp0_aclkx", "uart5_txd", "pr1_mii0_rxd3", "uart2_ctsn", "gpio2_14"), + _PIN(0x8c4, "LCD_DATA9", 76, 7, "lcd_data9", "gpmc_a13", "ehrpwm0_synco", "mcasp0_fsx", "uart5_rxd", "pr1_mii0_rxd2", "uart2_rtsn", "gpio2_15"), + _PIN(0x8c8, "LCD_DATA10", 77, 7, "lcd_data10", "gpmc_a14", "ehrpwm1A", "mcasp0_axr0", NULL, "pr1_mii0_rxd1", "uart3_ctsn", "gpio2_16"), + _PIN(0x8cc, "LCD_DATA11", 78, 7, "lcd_data11", "gpmc_a15", "ehrpwm1B", "mcasp0_ahclkr", "mcasp0_axr2", "pr1_mii0_rxd0", "uart3_rtsn", "gpio2_17"), + _PIN(0x8d0, "LCD_DATA12", 8, 7, "lcd_data12", "gpmc_a16", "eQEP1A_in", "mcasp0_aclkr", "mcasp0_axr2", "pr1_mii0_rxlink", "uart4_ctsn", "gpio0_8"), + _PIN(0x8d4, "LCD_DATA13", 9, 7, "lcd_data13", "gpmc_a17", "eQEP1B_in", "mcasp0_fsr", "mcasp0_axr3", "pr1_mii0_rxer", "uart4_rtsn", "gpio0_9"), + _PIN(0x8d8, "LCD_DATA14", 10, 7, "lcd_data14", "gpmc_a18", "eQEP1_index", "mcasp0_axr1", "uart5_rxd", "pr1_mii_mr0_clk", "uart5_ctsn", "gpio0_10"), + _PIN(0x8dc, "LCD_DATA15", 11, 7, "lcd_data15", "gpmc_a19", "eQEP1_strobe", "mcasp0_ahclkx", "mcasp0_axr3", "pr1_mii0_rxdv", "uart5_rtsn", "gpio0_11"), + _PIN(0x8e0, "LCD_VSYNC", 86, 7, "lcd_vsync", "gpmc_a8", "gpmc_a1", "pr1_edio_data_in2", "pr1_edio_data_out2", "pr1_pru1_pru_r30_8", "pr1_pru1_pru_r31_8", "gpio2_22"), + _PIN(0x8e4, "LCD_HSYNC", 87, 7, "lcd_hsync", "gmpc_a9", "gpmc_a2", "pr1_edio_data_in3", "pr1_edio_data_out3", "pr1_pru1_pru_r30_9", "pr1_pru1_pru_r31_9", "gpio2_23"), + _PIN(0x8e8, "LCD_PCLK", 88, 7, "lcd_pclk", "gpmc_a10", "pr1_mii0_crs", "pr1_edio_data_in4", "pr1_edio_data_out4", "pr1_pru1_pru_r30_10", "pr1_pru1_pru_r31_10", "gpio2_24"), + _PIN(0x8ec, "LCD_AC_BIAS_EN", 89, 7, "lcd_ac_bias_en", "gpmc_a11", "pr1_mii1_crs", "pr1_edio_data_in5", "pr1_edio_data_out5", "pr1_pru1_pru_r30_11", "pr1_pru1_pru_r31_11", "gpio2_25"), + _PIN(0x8f0, "MMC0_DAT3", 90, 7, "mmc0_dat3", "gpmc_a20", "uart4_ctsn", "timer5", "uart1_dcdn", "pr1_pru0_pru_r30_8", "pr1_pru0_pru_r31_8", "gpio2_26"), + _PIN(0x8f4, "MMC0_DAT2", 91, 7, "mmc0_dat2", "gpmc_a21", "uart4_rtsn", "timer6", "uart1_dsrn", "pr1_pru0_pru_r30_9", "pr1_pru0_pru_r31_9", "gpio2_27"), + _PIN(0x8f8, "MMC0_DAT1", 92, 7, "mmc0_dat1", "gpmc_a22", "uart5_ctsn", "uart3_rxd", "uart1_dtrn", "pr1_pru0_pru_r30_10", "pr1_pru0_pru_r31_10", "gpio2_28"), + _PIN(0x8fc, "MMC0_DAT0", 93, 7, "mmc0_dat0", "gpmc_a23", "uart5_rtsn", "uart3_txd", "uart1_rin", "pr1_pru0_pru_r30_11", "pr1_pru0_pru_r31_11", "gpio2_29"), + _PIN(0x900, "MMC0_CLK", 94, 7, "mmc0_clk", "gpmc_a24", "uart3_ctsn", "uart2_rxd", "dcan1_tx", "pr1_pru0_pru_r30_12", "pr1_pru0_pru_r31_12", "gpio2_30"), + _PIN(0x904, "MMC0_CMD", 95, 7, "mmc0_cmd", "gpmc_a25", "uart3_rtsn", "uart2_txd", "dcan1_rx", "pr1_pru0_pru_r30_13", "pr1_pru0_pru_r31_13", "gpio2_31"), + _PIN(0x908, "MII1_COL", 96, 7, "gmii1_col", "rmii2_refclk", "spi1_sclk", "uart5_rxd", "mcasp1_axr2", "mmc2_dat3", "mcasp0_axr2", "gpio3_0"), + _PIN(0x90c, "MII1_CRS", 97, 7, "gmii1_crs", "rmii1_crs_dv", "spi1_d0", "I2C1_SDA", "mcasp1_aclkx", "uart5_ctsn", "uart2_rxd", "gpio3_1"), + _PIN(0x910, "MII1_RX_ER", 98, 7, "gmii1_rxerr", "rmii1_rxerr", "spi1_d1", "I2C1_SCL", "mcasp1_fsx", "uart5_rtsn", "uart2_txd", "gpio3_2"), + _PIN(0x914, "MII1_TX_EN", 99, 7, "gmii1_txen", "rmii1_txen", "rgmii1_tctl", "timer4", "mcasp1_axr0", "eQEP0_index", "mmc2_cmd", "gpio3_3"), + _PIN(0x918, "MII1_RX_DV", 100, 7, "gmii1_rxdv", "cd_memory_clk", "rgmii1_rctl", "uart5_txd", "mcasp1_aclkx", "mmc2_dat0", "mcasp0_aclkr", "gpio3_4"), + _PIN(0x91c, "MII1_TXD3", 16, 7, "gmii1_txd3", "dcan0_tx", "rgmii1_td3", "uart4_rxd", "mcasp1_fsx", "mmc2_dat1", "mcasp0_fsr", "gpio0_16"), + _PIN(0x920, "MII1_TXD2", 17, 7, "gmii1_txd2", "dcan0_rx", "rgmii1_td2", "uart4_txd", "mcasp1_axr0", "mmc2_dat2", "mcasp0_ahclkx", "gpio0_17"), + _PIN(0x924, "MII1_TXD1", 21, 7, "gmii1_txd1", "rmii1_txd1", "rgmii1_td1", "mcasp1_fsr", "mcasp1_axr1", "eQEP0A_in", "mmc1_cmd", "gpio0_21"), + _PIN(0x928, "MII1_TXD0", 28, 7, "gmii1_txd0", "rmii1_txd0", "rgmii1_td0", "mcasp1_axr2", "mcasp1_aclkr", "eQEP0B_in", "mmc1_clk", "gpio0_28"), + _PIN(0x92c, "MII1_TX_CLK", 105, 7, "gmii1_txclk", "uart2_rxd", "rgmii1_tclk", "mmc0_dat7", "mmc1_dat0", "uart1_dcdn", "mcasp0_aclkx", "gpio3_9"), + _PIN(0x930, "MII1_RX_CLK", 106, 7, "gmii1_rxclk", "uart2_txd", "rgmii1_rclk", "mmc0_dat6", "mmc1_dat1", "uart1_dsrn", "mcasp0_fsx", "gpio3_10"), + _PIN(0x934, "MII1_RXD3", 82, 7, "gmii1_rxd3", "uart3_rxd", "rgmii1_rd3", "mmc0_dat5", "mmc1_dat2", "uart1_dtrn", "mcasp0_axr0", "gpio2_18"), + _PIN(0x938, "MII1_RXD2", 83, 7, "gmii1_rxd2", "uart3_txd", "rgmii1_rd2", "mmc0_dat4", "mmc1_dat3", "uart1_rin", "mcasp0_axr1", "gpio2_19"), + _PIN(0x93c, "MII1_RXD1", 84, 7, "gmii1_rxd1", "rmii1_rxd1", "rgmii1_rd1", "mcasp1_axr3", "mcasp1_fsr", "eQEP0_strobe", "mmc2_clk", "gpio2_20"), + _PIN(0x940, "MII1_RXD0", 85, 7, "gmii1_rxd0", "rmii1_rxd0", "rgmii1_rd0", "mcasp1_ahclkx", "mcasp1_ahclkr", "mcasp1_aclkr", "mcasp0_axr3", "gpio2_21"), + _PIN(0x944, "RMII1_REF_CLK", 29, 7, "rmii1_refclk", "xdma_event_intr2", "spi1_cs0", "uart5_txd", "mcasp1_axr3", "mmc0_pow", "mcasp1_ahclkx", "gpio0_29"), + _PIN(0x948, "MDIO", 0, 7, "mdio_data", "timer6", "uart5_rxd", "uart3_ctsn", "mmc0_sdcd","mmc1_cmd", "mmc2_cmd","gpio0_0"), + _PIN(0x94c, "MDC", 1, 7, "mdio_clk", "timer5", "uart5_txd", "uart3_rtsn", "mmc0_sdwp", "mmc1_clk", "mmc2_clk", "gpio0_1"), + _PIN(0x950, "SPI0_SCLK", 2, 7, "spi0_sclk", "uart2_rxd", "I2C2_SDA", "ehrpwm0A", "pr1_uart0_cts_n", "pr1_edio_sof", "EMU2", "gpio0_2"), + _PIN(0x954, "SPI0_D0", 3, 7, "spi0_d0", "uart2_txd", "I2C2_SCL", "ehrpwm0B", "pr1_uart0_rts_n", "pr1_edio_latch_in", "EMU3", "gpio0_3"), + _PIN(0x958, "SPIO_D1", 4, 7, "spi0_d1", "mmc1_sdwp", "I2C1_SDA", "ehrpwm0_tripzone_input", "pr1_uart0_rxd", "pr1_edio_data_in0", "pr1_edio_data_out0", "gpio0_4"), + _PIN(0x95c, "SPI0_CS0", 5, 7, "spi0_cs0", "mmc2_sdwp", "I2C1_SCL", "ehrpwm0_synci", "pr1_uart0_txd", "pr1_edio_data_in1", "pr1_edio_data_out1", "gpio0_5"), + _PIN(0x960, "SPI0_CS1", 6, 7, "spi0_cs1", "uart3_rxd", "eCAP1_in_PWM1_out", "mcc0_pow", "xdm_event_intr2", "mmc0_sdcd", "EMU4", "gpio0_6"), + _PIN(0x964, "ECAP0_IN_PWM0_OUT",7, 7, "eCAP0_in_PWM0_out", "uart3_txd", "spi1_cs1", "pr1_ecap0_ecap_capin_apwm_o", "spi1_sclk", "mmc0_sdwp", "xdma_event_intr2", "gpio0_7"), + _PIN(0x968, "UART0_CTSn", 40, 7, "uart0_ctsn", "uart4_rxd", "dcan1_tx", "I2C1_SDA", "spi1_d0", "timer7", "pr1_edc_sync0_out", "gpio1_8"), + _PIN(0x96c, "UART0_RTSn", 41, 7, "uart0_rtsn", "uart4_txd", "dcan1_rx", "I2C1_SCL", "spi1_d1", "spi1_cs0", "pr1_edc_sync1_out", "gpio1_9"), + _PIN(0x970, "UART0_rxd", 42, 7, "uart0_rxd", "spi1_cs0", "dcan0_tx", "I2C2_SDA", "eCAP2_in_PWM2_out", "pr1_pru1_pru_r30_14", "pr1_pru1_pru_r31_14", "gpio1_10"), + _PIN(0x974, "UART0_txd", 43, 7, "uart0_txd", "spi1_cs1", "dcan0_rx", "I2C2_SCL", "eCAP1_in_PWM1_out", "pr1_pru1_pru_r30_15", "pr1_pru1_pru_r31_15", "gpio1_11"), + _PIN(0x978, "UART1_CTSn", 12, 7, "uart1_ctsn", "timer6_mux1", "dcan0_tx", "I2C2_SDA", "spi1_cs0", "pr1_uart0_cts_n", "pr1_edc_latch0_in", "gpio0_12"), + _PIN(0x97c, "UART1_RTSn", 13, 7, "uart1_rtsn", "timer5_mux1", "dcan0_rx", "I2C2_SCL", "spi1_cs1", "pr1_uart0_rts_n ", "pr1_edc_latch1_in", "gpio0_13"), + _PIN(0x980, "UART1_RXD", 14, 7, "uart1_rxd", "mmc1_sdwp", "dcan1_tx", "I2C1_SDA", NULL, "pr1_uart0_rxd", "pr1_pru1_pru_r31_16", "gpio0_14"), + _PIN(0x984, "UART1_TXD", 15, 7, "uart1_txd", "mmc2_sdwp", "dcan1_rx", "I2C1_SCL", NULL, "pr1_uart0_txd", "pr1_pru0_pru_r31_16", "gpio0_15"), + _PIN(0x988, "I2C0_SDA", 101, 7, "I2C0_SDA", "timer4", "uart2_ctsn", "eCAP2_in_PWM2_out", NULL, NULL, NULL, "gpio3_5"), + _PIN(0x98c, "I2C0_SCL", 102, 7, "I2C0_SCL", "timer7", "uart2_rtsn", "eCAP1_in_PWM1_out", NULL, NULL, NULL, "gpio3_6"), + _PIN(0x990, "MCASP0_ACLKX", 110, 7, "mcasp0_aclkx", "ehrpwm0A", NULL, "spi1_sclk", "mmc0_sdcd", "pr1_pru0_pru_r30_0", "pr1_pru0_pru_r31_0", "gpio3_14"), + _PIN(0x994, "MCASP0_FSX", 111, 7, "mcasp0_fsx", "ehrpwm0B", NULL, "spi1_d0", "mmc1_sdcd", "pr1_pru0_pru_r30_1", "pr1_pru0_pru_r31_1", "gpio3_15"), + _PIN(0x998, "MCASP0_AXR0", 112, 7, "mcasp0_axr0", "ehrpwm0_tripzone_input", NULL, "spi1_d1", "mmc2_sdcd", "pr1_pru0_pru_r30_2", "pr1_pru0_pru_r31_2", "gpio3_16"), + _PIN(0x99c, "MCASP0_AHCLKR", 113, 7, "mcasp0_ahclkr", "ehrpwm0_synci", "mcasp0_axr2", "spi1_cs0", "eCAP2_in_PWM2_out", "pr1_pru0_pru_r30_3", "pr1_pru0_pru_r31_3", "gpio3_17"), + _PIN(0x9a0, "MCASP0_ACLKR", 114, 7, "mcasp0_aclkr", "eQEP0A_in", "mcasp0_axr2", "mcasp1_aclkx", "mmc0_sdwp", "pr1_pru0_pru_r30_4", "pr1_pru0_pru_r31_4", "gpio3_18"), + _PIN(0x9a4, "MCASP0_FSR", 115, 7, "mcasp0_fsr", "eQEP0B_in", "mcasp0_axr3", "mcasp1_fsx", "EMU2", "pr1_pru0_pru_r30_5", "pr1_pru0_pru_r31_5", "gpio3_19"), + _PIN(0x9a8, "MCASP0_AXR1", 116, 7, "mcasp0_axr1", "eQEP0_index", NULL, "mcasp1_axr0", "EMU3", "pr1_pru0_pru_r30_6", "pr1_pru0_pru_r31_6", "gpio3_20"), + _PIN(0x9ac, "MCASP0_AHCLKX", 117, 7, "mcasp0_ahclkx", "eQEP0_strobe", "mcasp0_axr3", "mcasp1_axr1", "EMU4", "pr1_pru0_pru_r30_7", "pr1_pru0_pru_r31_7", "gpio3_21"), + _PIN(0x9b0, "XDMA_EVENT_INTR0", 19, 7, "xdma_event_intr0", NULL, "timer4", "clkout1", "spi1_cs1", "pr1_pru1_pru_r31_16", "EMU2", "gpio0_19"), + _PIN(0x9b4, "XDMA_EVENT_INTR1", 20, 7, "xdma_event_intr1", NULL, "tclkin", "clkout2", "timer7", "pr1_pru0_pru_r31_16", "EMU3", "gpio0_20"), +#if 0 + _PIN(0x9b8, "nresetin_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9bc, "porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c0, "nnmi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c4, "osc0_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9c8, "osc0_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9cc, "osc0_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d0, "tms", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d4, "tdi", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9d8, "tdo", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9dc, "tck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9e0, "ntrst", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0x9e4, "EMU0", 103, 7, "EMU0", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_7"), + _PIN(0x9e8, "EMU1", 104, 0, "EMU1", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_8"), +#if 0 + _PIN(0x9ec, "osc1_in", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f0, "osc1_out", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f4, "osc1_vss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9f8, "rtc_porz", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0x9fc, "pmic_power_en", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa00, "ext_wakeup", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa04, "enz_kaldo_1p8v", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + _PIN(0xa08, "USB0_DM", 0, 0, "USB0_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa0c, "USB0_DP", 0, 0, "USB0_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa10, "USB0_CE", 0, 0, "USB0_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa14, "USB0_ID", 0, 0, "USB0_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa18, "USB0_VBUS", 0, 0, "USB0_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa1c, "USB0_DRVVBUS", 18, 7, "USB0_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio0_18"), + _PIN(0xa20, "USB1_DM", 0, 0, "USB1_DM", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa24, "USB1_DP", 0, 0, "USB1_DP", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa28, "USB1_CE", 0, 0, "USB1_CE", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa2c, "USB1_ID", 0, 0, "USB1_ID", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa30, "USB1_VBUS", 0, 0, "USB1_VBUS", NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa34, "USB1_DRVVBUS", 109, 7, "USB1_DRVVBUS", NULL, NULL, NULL, NULL, NULL, NULL, "gpio3_13"), +#if 0 + _PIN(0xa38, "ddr_resetn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa3c, "ddr_csn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa40, "ddr_cke", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa44, "ddr_ck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa48, "ddr_nck", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa4c, "ddr_casn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa50, "ddr_rasn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa54, "ddr_wen", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa58, "ddr_ba0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa5c, "ddr_ba1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa60, "ddr_ba2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa64, "ddr_a0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa68, "ddr_a1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa6c, "ddr_a2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa70, "ddr_a3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa74, "ddr_a4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa78, "ddr_a5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa7c, "ddr_a6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa80, "ddr_a7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa84, "ddr_a8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa88, "ddr_a9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa8c, "ddr_a10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa90, "ddr_a11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa94, "ddr_a12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa98, "ddr_a13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xa9c, "ddr_a14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa0, "ddr_a15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa4, "ddr_odt", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaa8, "ddr_d0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaac, "ddr_d1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab0, "ddr_d2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab4, "ddr_d3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xab8, "ddr_d4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xabc, "ddr_d5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac0, "ddr_d6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac4, "ddr_d7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xac8, "ddr_d8", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xacc, "ddr_d9", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad0, "ddr_d10", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad4, "ddr_d11", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xad8, "ddr_d12", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xadc, "ddr_d13", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae0, "ddr_d14", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae4, "ddr_d15", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xae8, "ddr_dqm0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaec, "ddr_dqm1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf0, "ddr_dqs0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf4, "ddr_dqsn0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xaf8, "ddr_dqs1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xafc, "ddr_dqsn1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb00, "ddr_vref", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb04, "ddr_vtp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb08, "ddr_strben0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb0c, "ddr_strben1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb2c, "ain0", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb28, "ain1", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb24, "ain2", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb20, "ain3", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb1c, "ain4", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb18, "ain5", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb14, "ain6", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb10, "ain7", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb30, "vrefp", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb34, "vrefn", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb38, "avdd", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb3c, "avss", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb40, "iforce", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb44, "vsense", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + _PIN(0xb48, "testout", 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +#endif + { .ballname = NULL }, +}; + +const struct sitara_cm_device sitara_cm_dev = { + .padconf_muxmode_mask = 0x7, + .padconf_sate_mask = 0x78, + .padstate = (struct sitara_cm_padstate *) &ti_padstate_devmap, + .padconf = (struct sitara_cm_padconf *) &ti_padconf_devmap, +}; + +int +sitara_cm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags) +{ + unsigned int state = 0; + 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 sitara_cm_padconf_set_gpiomode(gpio, state); +} + +void +sitara_cm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags) +{ + unsigned int state; + if (sitara_cm_padconf_get_gpiomode(gpio, &state) != 0) + *flags = 0; + 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; + } + } +} diff --git a/am335x_prcmreg.h b/am335x_prcmreg.h new file mode 100644 index 0000000..66f3fa7 --- /dev/null +++ b/am335x_prcmreg.h @@ -0,0 +1,57 @@ +/* $OpenBSD: am335x_prcmreg.h,v 1.4 2014/03/18 07:34:17 syl Exp $ */ +/* + * Copyright (c) 2013 Raphael Graf + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define AM335X_CLKCTRL_MODULEMODE_ENABLE 2 +#define AM335X_CLKCTRL_MODULEMODE_DISABLE 0 +#define AM335X_CLKCTRL_MODULEMODE_MASK 0x00000003 + +#define PRCM_AM335X_CM_PER 0x0000 +#define PRCM_AM335X_USB0_CLKCTRL 0x001c +#define PRCM_AM335X_TPTC0_CLKCTRL 0x0024 +#define PRCM_AM335X_MMC0_CLKCTRL 0x003c +#define PRCM_AM335X_I2C2_CLKCTRL 0x0044 +#define PRCM_AM335X_I2C1_CLKCTRL 0x0048 +#define PRCM_AM335X_TIMER2_CLKCTRL 0x0080 +#define PRCM_AM335X_TIMER3_CLKCTRL 0x0084 +#define PRCM_AM335X_GPIO1_CLKCTRL 0x00ac +#define PRCM_AM335X_GPIO2_CLKCTRL 0x00b0 +#define PRCM_AM335X_GPIO3_CLKCTRL 0x00b4 +#define PRCM_AM335X_TPCC_CLKCTRL 0x00bc +#define PRCM_AM335X_MMC1_CLKCTRL 0x00f4 +#define PRCM_AM335X_MMC2_CLKCTRL 0x00f8 +#define PRCM_AM335X_TPTC1_CLKCTRL 0x00fc +#define PRCM_AM335X_TPTC2_CLKCTRL 0x0100 +#define PRCM_AM335X_CM_WKUP 0x0400 +#define PRCM_AM335X_GPIO0_CLKCTRL 0x0408 +#define PRCM_AM335X_TIMER0_CLKCTRL 0x0410 +#define PRCM_AM335X_I2C0_CLKCTRL 0x04b8 +#define PRCM_AM335X_CM_DPLL 0x0500 +#define PRCM_AM335X_CLKSEL_TIMER2_CLK 0x0508 +#define PRCM_AM335X_CLKSEL_TIMER3_CLK 0x050c +#define PRCM_AM335X_CM_MPU 0x0600 +#define PRCM_AM335X_CM_DEVICE 0x0700 +#define PRCM_AM335X_CM_RTC 0x0800 +#define PRCM_AM335X_CM_GFX 0x0900 +#define PRCM_AM335X_CM_CEFUSE 0x0a00 +#define PRCM_AM335X_PRM_IRQ 0x0b00 +#define PRCM_AM335X_PRM_PER 0x0c00 +#define PRCM_AM335X_PRM_WKUP 0x0d00 +#define PRCM_AM335X_PRM_MPU 0x0e00 +#define PRCM_AM335X_PRM_DEVICE 0x0f00 +#define PRCM_AM335X_PRM_RTC 0x1000 +#define PRCM_AM335X_PRM_GFX 0x1100 +#define PRCM_AM335X_PRM_CEFUSE 0x1200 diff --git a/dmtimer.c b/dmtimer.c new file mode 100644 index 0000000..252b2fd --- /dev/null +++ b/dmtimer.c @@ -0,0 +1,413 @@ +/* $OpenBSD: dmtimer.c,v 1.6 2015/01/22 14:33:01 krw Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * Copyright (c) 2013 Raphael Graf + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * WARNING - this timer initializion has not been checked + * to see if it will do _ANYTHING_ sane if the omap enters + * low power mode. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* registers */ +#define DM_TIDR 0x000 +#define DM_TIDR_MAJOR 0x00000700 +#define DM_TIDR_MINOR 0x0000003f +#define DM_TIOCP_CFG 0x010 +#define DM_TIOCP_CFG_IDLEMODE (3<<2) +#define DM_TIOCP_CFG_EMUFREE (1<<1) +#define DM_TIOCP_CFG_SOFTRESET (1<<0) +#define DM_TISR 0x028 +#define DM_TISR_TCAR (1<<2) +#define DM_TISR_OVF (1<<1) +#define DM_TISR_MAT (1<<0) +#define DM_TIER 0x2c +#define DM_TIER_TCAR_EN (1<<2) +#define DM_TIER_OVF_EN (1<<1) +#define DM_TIER_MAT_EN (1<<0) +#define DM_TIECR 0x30 +#define DM_TIECR_TCAR_EN (1<<2) +#define DM_TIECR_OVF_EN (1<<1) +#define DM_TIECR_MAT_EN (1<<0) +#define DM_TWER 0x034 +#define DM_TWER_TCAR_EN (1<<2) +#define DM_TWER_OVF_EN (1<<1) +#define DM_TWER_MAT_EN (1<<0) +#define DM_TCLR 0x038 +#define DM_TCLR_GPO (1<<14) +#define DM_TCLR_CAPT (1<<13) +#define DM_TCLR_PT (1<<12) +#define DM_TCLR_TRG (3<<10) +#define DM_TCLR_TRG_O (1<<10) +#define DM_TCLR_TRG_OM (2<<10) +#define DM_TCLR_TCM (3<<8) +#define DM_TCLR_TCM_RISE (1<<8) +#define DM_TCLR_TCM_FALL (2<<8) +#define DM_TCLR_TCM_BOTH (3<<8) +#define DM_TCLR_SCPWM (1<<7) +#define DM_TCLR_CE (1<<6) +#define DM_TCLR_PRE (1<<5) +#define DM_TCLR_PTV (7<<2) +#define DM_TCLR_AR (1<<1) +#define DM_TCLR_ST (1<<0) +#define DM_TCRR 0x03c +#define DM_TLDR 0x040 +#define DM_TTGR 0x044 +#define DM_TWPS 0x048 +#define DM_TWPS_TMAR (1<<4) +#define DM_TWPS_TTGR (1<<3) +#define DM_TWPS_TLDR (1<<2) +#define DM_TWPS_TCLR (1<<0) +#define DM_TWPS_TCRR (1<<1) +#define DM_TWPS_ALL 0x1f +#define DM_TMAR 0x04c +#define DM_TCAR 0x050 +#define DM_TSICR 0x054 +#define DM_TSICR_POSTED (1<<2) +#define DM_TSICR_SFT (1<<1) +#define DM_TCAR2 0x058 + +#define TIMER_FREQUENCY 32768 /* 32kHz is used, selectable */ +#define MAX_TIMERS 2 + +static struct evcount clk_count; +static struct evcount stat_count; + +void dmtimer_attach(struct device *parent, struct device *self, void *args); +int dmtimer_intr(void *frame); +void dmtimer_wait(int reg); +void dmtimer_cpu_initclocks(void); +void dmtimer_delay(u_int); +void dmtimer_setstatclockrate(int newhz); + +u_int dmtimer_get_timecount(struct timecounter *); + +static struct timecounter dmtimer_timecounter = { + dmtimer_get_timecount, NULL, 0xffffffff, 0, "dmtimer", 0, NULL +}; + +bus_space_handle_t dmtimer_ioh0; +int dmtimer_irq = 0; + +struct dmtimer_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh[MAX_TIMERS]; + u_int32_t sc_irq; + u_int32_t sc_ticks_per_second; + u_int32_t sc_ticks_per_intr; + u_int32_t sc_ticks_err_cnt; + u_int32_t sc_ticks_err_sum; + u_int32_t sc_statvar; + u_int32_t sc_statmin; + u_int32_t sc_nexttickevent; + u_int32_t sc_nextstatevent; +}; + +struct cfattach dmtimer_ca = { + sizeof (struct dmtimer_softc), NULL, dmtimer_attach +}; + +struct cfdriver dmtimer_cd = { + NULL, "dmtimer", DV_DULL +}; + +void +dmtimer_attach(struct device *parent, struct device *self, void *args) +{ + struct dmtimer_softc *sc = (struct dmtimer_softc *)self; + struct armv7_attach_args *aa = args; + bus_space_handle_t ioh; + u_int32_t rev, cfg; + + sc->sc_iot = aa->aa_iot; + + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &ioh)) + panic("%s: bus_space_map failed!\n", __func__); + + + prcm_setclock(1, PRCM_CLK_SPEED_32); + prcm_setclock(2, PRCM_CLK_SPEED_32); + prcm_enablemodule(PRCM_TIMER2); + prcm_enablemodule(PRCM_TIMER3); + + /* reset */ + bus_space_write_4(sc->sc_iot, ioh, DM_TIOCP_CFG, + DM_TIOCP_CFG_SOFTRESET); + while (bus_space_read_4(sc->sc_iot, ioh, DM_TIOCP_CFG) + & DM_TIOCP_CFG_SOFTRESET) + ; + + if (self->dv_unit == 0) { + dmtimer_ioh0 = ioh; + dmtimer_irq = aa->aa_dev->irq[0]; + /* enable write posted mode */ + bus_space_write_4(sc->sc_iot, ioh, DM_TSICR, DM_TSICR_POSTED); + /* stop timer */ + bus_space_write_4(sc->sc_iot, ioh, DM_TCLR, 0); + } else if (self->dv_unit == 1) { + /* start timer because it is used in delay */ + /* interrupts and posted mode are disabled */ + sc->sc_irq = dmtimer_irq; + sc->sc_ioh[0] = dmtimer_ioh0; + sc->sc_ioh[1] = ioh; + + bus_space_write_4(sc->sc_iot, ioh, DM_TCRR, 0); + bus_space_write_4(sc->sc_iot, ioh, DM_TLDR, 0); + bus_space_write_4(sc->sc_iot, ioh, DM_TCLR, + DM_TCLR_AR | DM_TCLR_ST); + + dmtimer_timecounter.tc_frequency = TIMER_FREQUENCY; + dmtimer_timecounter.tc_priv = sc; + tc_init(&dmtimer_timecounter); + arm_clock_register(dmtimer_cpu_initclocks, dmtimer_delay, + dmtimer_setstatclockrate, NULL); + } + else + panic("attaching too many dmtimers at 0x%lx", + aa->aa_dev->mem[0].addr); + + /* set IDLEMODE to smart-idle */ + cfg = bus_space_read_4(sc->sc_iot, ioh, DM_TIOCP_CFG); + bus_space_write_4(sc->sc_iot, ioh, DM_TIOCP_CFG, + (cfg & ~DM_TIOCP_CFG_IDLEMODE) | 0x02); + + rev = bus_space_read_4(sc->sc_iot, ioh, DM_TIDR); + printf(" rev %d.%d\n", (rev & DM_TIDR_MAJOR) >> 8, rev & DM_TIDR_MINOR); +} + +/* + * See comment in arm/xscale/i80321_clock.c + * + * Counter is count up, but with autoreload timers it is not possible + * to detect how many interrupts passed while interrupts were blocked. + * Also it is not possible to atomically add to the register. + * + * To work around this two timers are used, one is used as a reference + * clock without reload, however we just disable the interrupt it + * could generate. + * + * Internally this keeps track of when the next timer should fire + * and based on that time and the current value of the reference + * clock a number is written into the timer count register to schedule + * the next event. + */ + +int +dmtimer_intr(void *frame) +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + u_int32_t now, r, nextevent; + int32_t duration; + + now = bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); + + while ((int32_t) (sc->sc_nexttickevent - now) <= 0) { + sc->sc_nexttickevent += sc->sc_ticks_per_intr; + sc->sc_ticks_err_sum += sc->sc_ticks_err_cnt; + + while (sc->sc_ticks_err_sum > hz) { + sc->sc_nexttickevent += 1; + sc->sc_ticks_err_sum -= hz; + } + + clk_count.ec_count++; + hardclock(frame); + } + + while ((int32_t) (sc->sc_nextstatevent - now) <= 0) { + do { + r = random() & (sc->sc_statvar - 1); + } while (r == 0); /* random == 0 not allowed */ + sc->sc_nextstatevent += sc->sc_statmin + r; + stat_count.ec_count++; + statclock(frame); + } + if ((sc->sc_nexttickevent - now) < (sc->sc_nextstatevent - now)) + nextevent = sc->sc_nexttickevent; + else + nextevent = sc->sc_nextstatevent; + + duration = nextevent - + bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); + + if (duration <= 0) + duration = 1; /* trigger immediately. */ + + if (duration > sc->sc_ticks_per_intr + 1) { + printf("%s: time lost!\n", __func__); + /* + * If interrupts are blocked too long, like during + * the root prompt or ddb, the timer can roll over, + * this will allow the system to continue to run + * even if time is lost. + */ + duration = sc->sc_ticks_per_intr; + sc->sc_nexttickevent = now; + sc->sc_nextstatevent = now; + } + + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR, + bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, -duration); + dmtimer_wait(DM_TWPS_ALL); + + return 1; +} + +/* + * would be interesting to play with trigger mode while having one timer + * in 32KHz mode, and the other timer running in sysclk mode and use + * the high resolution speeds (matters more for delay than tick timer + */ + +void +dmtimer_cpu_initclocks() +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + + stathz = 128; + profhz = 1024; + + sc->sc_ticks_per_second = TIMER_FREQUENCY; /* 32768 */ + + setstatclockrate(stathz); + + sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz; + sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz; + sc->sc_ticks_err_sum = 0; + + /* establish interrupts */ + arm_intr_establish(sc->sc_irq, IPL_CLOCK, dmtimer_intr, + NULL, "tick"); + + /* setup timer 0 */ + + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TLDR, 0); + + sc->sc_nexttickevent = sc->sc_nextstatevent = bus_space_read_4(sc->sc_iot, + sc->sc_ioh[1], DM_TCRR) + sc->sc_ticks_per_intr; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TIER, DM_TIER_OVF_EN); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TWER, DM_TWER_OVF_EN); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR, /*clear interrupt flags */ + bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, -sc->sc_ticks_per_intr); + dmtimer_wait(DM_TWPS_ALL); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCLR, /* autoreload and start */ + DM_TCLR_AR | DM_TCLR_ST); + dmtimer_wait(DM_TWPS_ALL); +} + +void +dmtimer_wait(int reg) +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + while (bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TWPS) & reg) + ; +} + +void +dmtimer_delay(u_int usecs) +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + u_int32_t clock, oclock, delta, delaycnt; + volatile int j; + int csec, usec; + + if (usecs > (0x80000000 / (TIMER_FREQUENCY))) { + csec = usecs / 10000; + usec = usecs % 10000; + + delaycnt = (TIMER_FREQUENCY / 100) * csec + + (TIMER_FREQUENCY / 100) * usec / 10000; + } else { + delaycnt = TIMER_FREQUENCY * usecs / 1000000; + } + if (delaycnt <= 1) + for (j = 100; j > 0; j--) + ; + + if (sc->sc_ioh[1] == 0) { + /* BAH */ + for (; usecs > 0; usecs--) + for (j = 100; j > 0; j--) + ; + return; + } + oclock = bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); + while (1) { + for (j = 100; j > 0; j--) + ; + clock = bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); + delta = clock - oclock; + if (delta > delaycnt) + break; + } + +} + +void +dmtimer_setstatclockrate(int newhz) +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + int minint, statint; + int s; + + s = splclock(); + + statint = sc->sc_ticks_per_second / newhz; + /* calculate largest 2^n which is smaller than just over half statint */ + sc->sc_statvar = 0x40000000; /* really big power of two */ + minint = statint / 2 + 100; + while (sc->sc_statvar > minint) + sc->sc_statvar >>= 1; + + sc->sc_statmin = statint - (sc->sc_statvar >> 1); + + splx(s); + + /* + * XXX this allows the next stat timer to occur then it switches + * to the new frequency. Rather than switching instantly. + */ +} + + +u_int +dmtimer_get_timecount(struct timecounter *tc) +{ + struct dmtimer_softc *sc = dmtimer_timecounter.tc_priv; + + return bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); +} diff --git a/edma.c b/edma.c new file mode 100644 index 0000000..333bcc8 --- /dev/null +++ b/edma.c @@ -0,0 +1,283 @@ +/* $OpenBSD: edma.c,v 1.5 2015/01/22 14:33:01 krw Exp $ */ +/* + * Copyright (c) 2013 Sylvestre Gallon + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#define DEVNAME(s) ((s)->sc_dev.dv_xname) + +struct edma_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_tpcc; + + void *sc_ih_comp; + edma_intr_cb_t sc_intr_cb[64]; + void *sc_intr_dat[64]; +}; + +#define EDMA_NUM_DMA_CHANS 64 +#define EDMA_NUM_QDMA_CHANS 8 +#define EDMA_TPCC_DHCM(x) (0x100 + (x * 4)) +#define EDMA_REG_X(x) (0x1000 + (0x200 * x)) +#define EDMA_TPCC_PID 0x0 +#define EDMA_TPCC_EMCR 0x308 +#define EDMA_TPCC_EMCRH 0x30c +#define EDMA_TPCC_CCERRCLR 0x31c +#define EDMA_TPCC_DRAE0 0x340 +#define EDMA_TPCC_DRAEH0 0x344 +#define EDMA_TPCC_ESR 0x1010 +#define EDMA_TPCC_ESRH 0x1014 +#define EDMA_TPCC_EESR 0x1030 +#define EDMA_TPCC_EESRH 0x1034 +#define EDMA_TPCC_SECR 0x1040 +#define EDMA_TPCC_SECRH 0x1044 +#define EDMA_TPCC_IER 0x1050 +#define EDMA_TPCC_IERH 0x1054 +#define EDMA_TPCC_IECR 0x1058 +#define EDMA_TPCC_IECRH 0x105c +#define EDMA_TPCC_IESR 0x1060 +#define EDMA_TPCC_IESRH 0x1064 +#define EDMA_TPCC_IPR 0x1068 +#define EDMA_TPCC_IPRH 0x106c +#define EDMA_TPCC_ICR 0x1070 +#define EDMA_TPCC_ICRH 0x1074 +#define EDMA_TPCC_IEVAL 0x1078 +#define EDMA_TPCC_OPT(x) (0x4000 + (x * 0x20)) + +#define TPCC_READ_4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_tpcc, (reg))) +#define TPCC_WRITE_4(sc, reg, val) \ + (bus_space_write_4((sc)->sc_iot, (sc)->sc_tpcc, (reg), (val))) +#define TPCC_SET(sc, reg, val) \ + (TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) | (val)))) +#define TPCC_FILTSET(sc, reg, val, filt) \ + (TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) & (filt)) | (val))) + +struct edma_softc *edma_sc; + +void edma_attach(struct device *, struct device *, void *); +int edma_comp_intr(void *); + +struct cfattach edma_ca = { + sizeof(struct edma_softc), NULL, edma_attach +}; + +struct cfdriver edma_cd = { + NULL, "edma", DV_DULL +}; + +void +edma_attach(struct device *parent, struct device *self, void *aux) +{ + struct armv7_attach_args *aa = aux; + struct edma_softc *sc = (struct edma_softc *)self; + uint32_t rev; + int i; + + sc->sc_iot = aa->aa_iot; + + /* Map Base address for TPCC and TPCTX */ + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_tpcc)) { + printf("%s: bus_space_map failed for TPCC\n", DEVNAME(sc)); + return ; + } + + /* Enable TPCC and TPTC0 in PRCM */ + prcm_enablemodule(PRCM_TPCC); + prcm_enablemodule(PRCM_TPTC0); + + rev = TPCC_READ_4(sc, EDMA_TPCC_PID); + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + + /* XXX IPL_VM ? */ + /* Enable interrupts line */ + sc->sc_ih_comp = arm_intr_establish(aa->aa_dev->irq[0], IPL_VM, + edma_comp_intr, sc, DEVNAME(sc)); + if (sc->sc_ih_comp == NULL) { + printf("%s: unable to establish interrupt comp\n", DEVNAME(sc)); + bus_space_unmap(sc->sc_iot, sc->sc_tpcc, + aa->aa_dev->mem[0].size); + return ; + } + + /* Set global softc */ + edma_sc = sc; + + /* Clear Event Missed Events */ + TPCC_WRITE_4(sc, EDMA_TPCC_EMCR, 0xffffffff); + TPCC_WRITE_4(sc, EDMA_TPCC_EMCRH, 0xffffffff); + TPCC_WRITE_4(sc, EDMA_TPCC_CCERRCLR, 0xffffffff); + + /* Identity Map Channels PaRAM */ + for (i = 0; i < EDMA_NUM_DMA_CHANS; i++) + TPCC_WRITE_4(sc, EDMA_TPCC_DHCM(i), i << 5); + + /* + * Enable SHADOW Region 0 and only use this region + * This is needed to have working intr... + */ + TPCC_WRITE_4(sc, EDMA_TPCC_DRAE0, 0xffffffff); + TPCC_WRITE_4(sc, EDMA_TPCC_DRAEH0, 0xffffffff); + + return ; +} + +int +edma_comp_intr(void *arg) +{ + struct edma_softc *sc = arg; + uint32_t ipr, iprh; + int i; + + ipr = TPCC_READ_4(sc, EDMA_TPCC_IPR); + iprh = TPCC_READ_4(sc, EDMA_TPCC_IPRH); + + /* Lookup to intr in the first 32 chans */ + for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) { + if (ISSET(ipr, (1<sc_intr_cb[i]) + sc->sc_intr_cb[i](sc->sc_intr_dat[i]); + } + } + + for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) { + if (ISSET(iprh, (1<sc_intr_cb[i + 32]) + sc->sc_intr_cb[i + 32](sc->sc_intr_dat[i + 32]); + } + } + + /* Trig pending intr */ + TPCC_WRITE_4(sc, EDMA_TPCC_IEVAL, 1); + + return (1); +} + +int +edma_intr_dma_en(uint32_t ch, edma_intr_cb_t cb, void *dat) +{ + if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS) + return (EINVAL); + + edma_sc->sc_intr_cb[ch] = cb; + edma_sc->sc_intr_dat[ch] = dat; + + if (ch < 32) { + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR + EDMA_REG_X(0), 1 << ch); + } else { + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH + EDMA_REG_X(0), + 1 << (ch - 32)); + } + + return (0); +} + +int +edma_intr_dma_dis(uint32_t ch) +{ + if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS) + return (EINVAL); + + if (ch < 32) + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECR, 1 << ch); + else + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECRH, 1 << (ch - 32)); + edma_sc->sc_intr_cb[ch] = NULL; + edma_sc->sc_intr_dat[ch] = NULL; + + return (0); +} + +int +edma_trig_xfer_man(uint32_t ch) +{ + if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS) + return (EINVAL); + + /* + * Trig xfer + * enable IEVAL only if there is an intr associated + */ + if (ch < 32) { + if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch)) + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESR, 1 << ch); + } else { + if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32))) + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESRH, 1 << (ch - 32)); + } + + return (0); +} + +int +edma_trig_xfer_by_dev(uint32_t ch) +{ + if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS) + return (EINVAL); + + if (ch < 32) { + if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch)) + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESR, 1 << ch); + } else { + if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32))) + TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32)); + TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESRH, 1 << (ch - 32)); + } + return (0); +} + +void +edma_param_write(uint32_t ch, struct edma_param *params) +{ + bus_space_write_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc, + EDMA_TPCC_OPT(ch), (uint32_t *)params, 8); +} + +void +edma_param_read(uint32_t ch, struct edma_param *params) +{ + bus_space_read_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc, + EDMA_TPCC_OPT(ch), (uint32_t *)params, 8); +} + diff --git a/edmavar.h b/edmavar.h new file mode 100644 index 0000000..e1a7ed3 --- /dev/null +++ b/edmavar.h @@ -0,0 +1,49 @@ +/* $OpenBSD: edmavar.h,v 1.4 2015/01/22 14:33:01 krw Exp $ */ +/* + * Copyright (c) 2013 Sylvestre Gallon + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __EDMAVAR_H__ +#define __EDMAVAR_H__ + +typedef void (*edma_intr_cb_t)(void *); + +/* + * EDMA PaRAM dma descriptors + */ +struct edma_param{ + uint32_t opt; /* Option */ + uint32_t src; /* Ch source */ + uint16_t acnt; /* 1st dim count */ + uint16_t bcnt; /* 2nd dim count */ + uint32_t dst; /* Chan dst addr */ + int16_t srcbidx; /* Src b index */ + int16_t dstbidx; /* Dst b index */ + uint16_t link; /* Link addr */ + uint16_t bcntrld; /* BCNT reload */ + int16_t srccidx; /* Source C index */ + int16_t dstcidx; /* Dest C index */ + uint16_t ccnt; /* 3rd dim count */ + uint16_t res; /* Reserved */ +} __attribute__((__packed__)); + +int edma_intr_dma_en(uint32_t, edma_intr_cb_t, void *); /* en it for chan */ +int edma_intr_dma_dis(uint32_t); /* disable intr for chan */ +int edma_trig_xfer_man(uint32_t); /* trig a dma xfer */ +int edma_trig_xfer_by_dev(uint32_t); /* dma xfer trig by dev */ +void edma_param_write(uint32_t, struct edma_param *); +void edma_param_read(uint32_t, struct edma_param *); + +#endif /* __EDMAVAR_H__ */ diff --git a/files.omap b/files.omap new file mode 100644 index 0000000..a50b93e --- /dev/null +++ b/files.omap @@ -0,0 +1,92 @@ +# $OpenBSD: files.omap,v 1.8 2016/05/02 08:15:55 patrick Exp $ + +define omap {} +device omap: omap +attach omap at fdt +file arch/armv7/omap/omap_machdep.c omap needs-flag +file arch/armv7/omap/omap.c omap +file arch/armv7/omap/omap3.c +file arch/armv7/omap/omap4.c +file arch/armv7/omap/am335x.c + +device ommmc: sdmmcbus +attach ommmc at omap +file arch/armv7/omap/ommmc.c ommmc + +device cpsw: ether, ifnet, mii, ifmedia +attach cpsw at omap +file arch/armv7/omap/if_cpsw.c cpsw + +device prcm +attach prcm at omap +file arch/armv7/omap/prcm.c prcm + +device sitaracm +attach sitaracm at omap +file arch/armv7/omap/am335x_cm_padconf.c sitaracm +file arch/armv7/omap/sitara_cm.c sitaracm + +device omgpio: gpiobus +attach omgpio at omap +file arch/armv7/omap/omgpio.c omgpio + +device tiiic: i2cbus +attach tiiic at omap +file arch/armv7/omap/ti_iic.c tiiic + +device edma +attach edma at omap +file arch/armv7/omap/edma.c edma + +device intc +attach intc at omap +file arch/armv7/omap/intc.c intc + +device gptimer +attach gptimer at omap +file arch/armv7/omap/gptimer.c gptimer + +device dmtimer +attach dmtimer at omap +file arch/armv7/omap/dmtimer.c dmtimer + +device omapid +attach omapid at omap +file arch/armv7/omap/omapid.c omapid + +device omdog +attach omdog at omap +file arch/armv7/omap/omdog.c omdog + +attach ohci at omap with omohci +file arch/armv7/omap/omohci.c omohci + +attach ehci at omap with omehci +file arch/armv7/omap/omehci.c omehci + +# NS16550 compatible serial ports +attach com at omap with com_omap +file arch/armv7/omap/omap_com.c com_omap + +device omusbtll +attach omusbtll at omap +file arch/armv7/omap/omusbtll.c omusbtll + +device omkbd: wskbddev +attach omkbd at omap +file arch/armv7/omap/omkbd.c omkbd + +# LCD frame buffer +device omdisplay: wsemuldisplaydev, rasops16 +attach omdisplay at omap +file arch/armv7/omap/omdisplay.c omdisplay + +# MCSPI - spi +device mcspi +attach mcspi at omap +file arch/armv7/omap/mcspi.c mcspi + +# pseudo-Audio Device Driver +device oaudio: audio +attach oaudio at omap # configure after Atlas Driver +file arch/armv7/omap/beagle_audio.c oaudio diff --git a/gptimer.c b/gptimer.c new file mode 100644 index 0000000..e5fd996 --- /dev/null +++ b/gptimer.c @@ -0,0 +1,441 @@ +/* $OpenBSD: gptimer.c,v 1.4 2014/06/20 14:08:11 rapha Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * WARNING - this timer initializion has not been checked + * to see if it will do _ANYTHING_ sane if the omap enters + * low power mode. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* registers */ +#define GP_TIDR 0x000 +#define GP_TIDR_REV 0xff +#define GP_TIOCP_CFG 0x010 +#define GP_TIOCP_CFG_CLKA 0x000000300 +#define GP_TIOCP_CFG_EMUFREE 0x000000020 +#define GP_TIOCP_CFG_IDLEMODE 0x000000018 +#define GP_TIOCP_CFG_ENAPWAKEUP 0x000000004 +#define GP_TIOCP_CFG_SOFTRESET 0x000000002 +#define GP_TIOCP_CFG_AUTOIDLE 0x000000001 +#define GP_TISTAT 0x014 +#define GP_TISTAT_RESETDONE 0x000000001 +#define GP_TISR 0x018 +#define GP_TISTAT_TCAR 0x00000004 +#define GP_TISTAT_OVF 0x00000002 +#define GP_TISTAT_MATCH 0x00000001 +#define GP_TIER 0x1c +#define GP_TIER_TCAR_EN 0x4 +#define GP_TIER_OVF_EN 0x2 +#define GP_TIER_MAT_EN 0x1 +#define GP_TWER 0x020 +#define GP_TWER_TCAR_EN 0x00000004 +#define GP_TWER_OVF_EN 0x00000002 +#define GP_TWER_MAT_EN 0x00000001 +#define GP_TCLR 0x024 +#define GP_TCLR_GPO (1<<14) +#define GP_TCLR_CAPT (1<<13) +#define GP_TCLR_PT (1<<12) +#define GP_TCLR_TRG (3<<10) +#define GP_TCLR_TRG_O (1<<10) +#define GP_TCLR_TRG_OM (2<<10) +#define GP_TCLR_TCM (3<<8) +#define GP_TCLR_TCM_RISE (1<<8) +#define GP_TCLR_TCM_FALL (2<<8) +#define GP_TCLR_TCM_BOTH (3<<8) +#define GP_TCLR_SCPWM (1<<7) +#define GP_TCLR_CE (1<<6) +#define GP_TCLR_PRE (1<<5) +#define GP_TCLR_PTV (7<<2) +#define GP_TCLR_AR (1<<1) +#define GP_TCLR_ST (1<<0) +#define GP_TCRR 0x028 /* counter */ +#define GP_TLDR 0x02c /* reload */ +#define GP_TTGR 0x030 +#define GP_TWPS 0x034 +#define GP_TWPS_TCLR 0x01 +#define GP_TWPS_TCRR 0x02 +#define GP_TWPS_TLDR 0x04 +#define GP_TWPS_TTGR 0x08 +#define GP_TWPS_TMAR 0x10 +#define GP_TWPS_ALL 0x1f +#define GP_TMAR 0x038 +#define GP_TCAR 0x03C +#define GP_TSICR 0x040 +#define GP_TSICR_POSTED 0x00000002 +#define GP_TSICR_SFT 0x00000001 +#define GP_TCAR2 0x044 + +#define TIMER_FREQUENCY 32768 /* 32kHz is used, selectable */ + +static struct evcount clk_count; +static struct evcount stat_count; +#define GPT1_IRQ 38 +#define GPTIMER0_IRQ 38 + +//static int clk_irq = GPT1_IRQ; /* XXX 37 */ + +void gptimer_attach(struct device *parent, struct device *self, void *args); +int gptimer_intr(void *frame); +void gptimer_wait(int reg); +void gptimer_cpu_initclocks(void); +void gptimer_delay(u_int); +void gptimer_setstatclockrate(int newhz); + +bus_space_tag_t gptimer_iot; +bus_space_handle_t gptimer_ioh0, gptimer_ioh1; +int gptimer_irq = 0; + +u_int gptimer_get_timecount(struct timecounter *); + +static struct timecounter gptimer_timecounter = { + gptimer_get_timecount, NULL, 0x7fffffff, 0, "gptimer", 0, NULL +}; + +volatile u_int32_t nexttickevent; +volatile u_int32_t nextstatevent; +u_int32_t ticks_per_second; +u_int32_t ticks_per_intr; +u_int32_t ticks_err_cnt; +u_int32_t ticks_err_sum; +u_int32_t statvar, statmin; + +struct cfattach gptimer_ca = { + sizeof (struct device), NULL, gptimer_attach +}; + +struct cfdriver gptimer_cd = { + NULL, "gptimer", DV_DULL +}; + +void +gptimer_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + bus_space_handle_t ioh; + u_int32_t rev; + + gptimer_iot = aa->aa_iot; + if (bus_space_map(gptimer_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &ioh)) + panic("gptimer_attach: bus_space_map failed!"); + + rev = bus_space_read_4(gptimer_iot, ioh, GP_TIDR); + + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + if (self->dv_unit == 0) { + gptimer_ioh0 = ioh; + gptimer_irq = aa->aa_dev->irq[0]; + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, 0); + } else if (self->dv_unit == 1) { + /* start timer because it is used in delay */ + gptimer_ioh1 = ioh; + bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TCRR, 0); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TLDR, 0); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TCLR, + GP_TCLR_AR | GP_TCLR_ST); + gptimer_wait(GP_TWPS_ALL); + + gptimer_timecounter.tc_frequency = TIMER_FREQUENCY; + tc_init(&gptimer_timecounter); + } + else + panic("attaching too many gptimers at 0x%lx", + aa->aa_dev->mem[0].addr); + + arm_clock_register(gptimer_cpu_initclocks, gptimer_delay, + gptimer_setstatclockrate, NULL); +} + +/* + * See comment in arm/xscale/i80321_clock.c + * + * counter is count up, but with autoreload timers it is not possible + * to detect how many interrupts passed while interrupts were blocked. + * also it is not possible to atomically add to the register + * get get it to precisely fire at a non-fixed interval. + * + * To work around this two timers are used, GPT1 is used as a reference + * clock without reload , however we just ignore the interrupt it + * would (may?) generate. + * + * Internally this keeps track of when the next timer should fire + * and based on that time and the current value of the reference + * clock a number is written into the timer count register to schedule + * the next event. + */ + +int +gptimer_intr(void *frame) +{ + u_int32_t now, r; + u_int32_t nextevent, duration; + + /* clear interrupt */ + now = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); + + while ((int32_t) (nexttickevent - now) < 0) { + nexttickevent += ticks_per_intr; + ticks_err_sum += ticks_err_cnt; +#if 0 + if (ticks_err_sum > hz) { + u_int32_t match_error; + match_error = ticks_err_sum / hz + ticks_err_sum -= (match_error * hz); + } +#else + /* looping a few times is faster than divide */ + while (ticks_err_sum > hz) { + nexttickevent += 1; + ticks_err_sum -= hz; + } +#endif + clk_count.ec_count++; + hardclock(frame); + } + while ((int32_t) (nextstatevent - now) < 0) { + do { + r = random() & (statvar -1); + } while (r == 0); /* random == 0 not allowed */ + nextstatevent += statmin + r; + /* XXX - correct nextstatevent? */ + stat_count.ec_count++; + statclock(frame); + } + if ((nexttickevent - now) < (nextstatevent - now)) + nextevent = nexttickevent; + else + nextevent = nextstatevent; + +/* XXX */ + duration = nextevent - + bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); +#if 0 + printf("duration 0x%x %x %x\n", nextevent - + bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR), + bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TCRR), + bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR)); +#endif + + + if (duration <= 0) + duration = 1; /* trigger immediately. */ + + if (duration > ticks_per_intr) { + /* + * If interrupts are blocked too long, like during + * the root prompt or ddb, the timer can roll over, + * this will allow the system to continue to run + * even if time is lost. + */ + duration = ticks_per_intr; + nexttickevent = now; + nextstatevent = now; + } + + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TISR, + bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TISR)); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCRR, -duration); + + return 1; +} + +/* + * would be interesting to play with trigger mode while having one timer + * in 32KHz mode, and the other timer running in sysclk mode and use + * the high resolution speeds (matters more for delay than tick timer + */ + +void +gptimer_cpu_initclocks() +{ +// u_int32_t now; + stathz = 128; + profhz = 1024; + + ticks_per_second = TIMER_FREQUENCY; + + setstatclockrate(stathz); + + ticks_per_intr = ticks_per_second / hz; + ticks_err_cnt = ticks_per_second % hz; + ticks_err_sum = 0;; + + prcm_setclock(1, PRCM_CLK_SPEED_32); + prcm_setclock(2, PRCM_CLK_SPEED_32); + /* establish interrupts */ + arm_intr_establish(gptimer_irq, IPL_CLOCK, gptimer_intr, + NULL, "tick"); + + /* setup timer 0 (hardware timer 2) */ + /* reset? - XXX */ + + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TLDR, 0); + + nexttickevent = nextstatevent = bus_space_read_4(gptimer_iot, + gptimer_ioh1, GP_TCRR) + ticks_per_intr; + + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TIER, GP_TIER_OVF_EN); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TWER, GP_TWER_OVF_EN); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, + GP_TCLR_AR | GP_TCLR_ST); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TISR, + bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TISR)); + gptimer_wait(GP_TWPS_ALL); + bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCRR, -ticks_per_intr); + gptimer_wait(GP_TWPS_ALL); +} + +void +gptimer_wait(int reg) +{ + while (bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TWPS) & reg) + ; +} + +#if 0 +void +microtime(struct timeval *tvp) +{ + int s; + int deltacnt; + u_int32_t counter, expected; + s = splhigh(); + + if (1) { /* not inited */ + tvp->tv_sec = 0; + tvp->tv_usec = 0; + return; + } + s = splhigh(); + counter = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); + expected = nexttickevent; + + *tvp = time; + splx(s); + + deltacnt = counter - expected + ticks_per_intr; + +#if 1 + /* low frequency timer algorithm */ + tvp->tv_usec += deltacnt * 1000000ULL / TIMER_FREQUENCY; +#else + /* high frequency timer algorithm - XXX */ + tvp->tv_usec += deltacnt / (TIMER_FREQUENCY / 1000000ULL); +#endif + + while (tvp->tv_usec >= 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + +} +#endif + +void +gptimer_delay(u_int usecs) +{ + u_int32_t clock, oclock, delta, delaycnt; + volatile int j; + int csec, usec; + + if (usecs > (0x80000000 / (TIMER_FREQUENCY))) { + csec = usecs / 10000; + usec = usecs % 10000; + + delaycnt = (TIMER_FREQUENCY / 100) * csec + + (TIMER_FREQUENCY / 100) * usec / 10000; + } else { + delaycnt = TIMER_FREQUENCY * usecs / 1000000; + } + if (delaycnt <= 1) + for (j = 100; j > 0; j--) + ; + + if (gptimer_ioh1 == 0) { + /* BAH */ + for (; usecs > 0; usecs--) + for (j = 100; j > 0; j--) + ; + return; + } + oclock = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); + while (1) { + for (j = 100; j > 0; j--) + ; + clock = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); + delta = clock - oclock; + if (delta > delaycnt) + break; + } + +} + +void +gptimer_setstatclockrate(int newhz) +{ + int minint, statint; + int s; + + s = splclock(); + + statint = ticks_per_second / newhz; + /* calculate largest 2^n which is smaller that just over half statint */ + statvar = 0x40000000; /* really big power of two */ + minint = statint / 2 + 100; + while (statvar > minint) + statvar >>= 1; + + statmin = statint - (statvar >> 1); + + splx(s); + + /* + * XXX this allows the next stat timer to occur then it switches + * to the new frequency. Rather than switching instantly. + */ +} + + +u_int +gptimer_get_timecount(struct timecounter *tc) +{ + return bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR); +} diff --git a/if_cpsw.c b/if_cpsw.c new file mode 100644 index 0000000..ad47506 --- /dev/null +++ b/if_cpsw.c @@ -0,0 +1,1254 @@ +/* $OpenBSD: if_cpsw.c,v 1.34 2016/04/13 11:33:59 mpi Exp $ */ +/* $NetBSD: if_cpsw.c,v 1.3 2013/04/17 14:36:34 bouyer Exp $ */ + +/* + * Copyright (c) 2013 Jonathan A. Kollasch + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + */ + +/*- + * 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 "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +#include +#include + +#include +#include +#include + +#define CPSW_TXFRAGS 16 + +#define OMAP2SCM_MAC_ID0_LO 0x630 +#define OMAP2SCM_MAC_ID0_HI 0x634 + +#define CPSW_CPPI_RAM_SIZE (0x2000) +#define CPSW_CPPI_RAM_TXDESCS_SIZE (CPSW_CPPI_RAM_SIZE/2) +#define CPSW_CPPI_RAM_RXDESCS_SIZE \ + (CPSW_CPPI_RAM_SIZE - CPSW_CPPI_RAM_TXDESCS_SIZE) +#define CPSW_CPPI_RAM_TXDESCS_BASE (CPSW_CPPI_RAM_OFFSET + 0x0000) +#define CPSW_CPPI_RAM_RXDESCS_BASE \ + (CPSW_CPPI_RAM_OFFSET + CPSW_CPPI_RAM_TXDESCS_SIZE) + +#define CPSW_NTXDESCS (CPSW_CPPI_RAM_TXDESCS_SIZE/sizeof(struct cpsw_cpdma_bd)) +#define CPSW_NRXDESCS (CPSW_CPPI_RAM_RXDESCS_SIZE/sizeof(struct cpsw_cpdma_bd)) + +#define CPSW_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +#define TXDESC_NEXT(x) cpsw_txdesc_adjust((x), 1) +#define TXDESC_PREV(x) cpsw_txdesc_adjust((x), -1) + +#define RXDESC_NEXT(x) cpsw_rxdesc_adjust((x), 1) +#define RXDESC_PREV(x) cpsw_rxdesc_adjust((x), -1) + +struct cpsw_ring_data { + bus_dmamap_t tx_dm[CPSW_NTXDESCS]; + struct mbuf *tx_mb[CPSW_NTXDESCS]; + bus_dmamap_t rx_dm[CPSW_NRXDESCS]; + struct mbuf *rx_mb[CPSW_NRXDESCS]; +}; + +struct cpsw_softc { + struct device sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_bdt; + bus_space_handle_t sc_bsh_txdescs; + bus_space_handle_t sc_bsh_rxdescs; + bus_addr_t sc_txdescs_pa; + bus_addr_t sc_rxdescs_pa; + + struct arpcom sc_ac; + struct mii_data sc_mii; + + struct cpsw_ring_data *sc_rdp; + volatile u_int sc_txnext; + volatile u_int sc_txhead; + volatile u_int sc_rxhead; + + void *sc_rxthih; + void *sc_rxih; + void *sc_txih; + void *sc_miscih; + + void *sc_txpad; + bus_dmamap_t sc_txpad_dm; +#define sc_txpad_pa sc_txpad_dm->dm_segs[0].ds_addr + + volatile bool sc_txrun; + volatile bool sc_rxrun; + volatile bool sc_txeoq; + volatile bool sc_rxeoq; + struct timeout sc_tick; + int sc_active_port; +}; + +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +void cpsw_attach(struct device *, struct device *, void *); + +void cpsw_start(struct ifnet *); +int cpsw_ioctl(struct ifnet *, u_long, caddr_t); +void cpsw_watchdog(struct ifnet *); +int cpsw_init(struct ifnet *); +void cpsw_stop(struct ifnet *); + +int cpsw_mii_readreg(struct device *, int, int); +void cpsw_mii_writereg(struct device *, int, int, int); +void cpsw_mii_statchg(struct device *); + +void cpsw_tick(void *); + +int cpsw_new_rxbuf(struct cpsw_softc * const, const u_int); +int cpsw_mediachange(struct ifnet *); +void cpsw_mediastatus(struct ifnet *, struct ifmediareq *); + +int cpsw_rxthintr(void *); +int cpsw_rxintr(void *); +int cpsw_txintr(void *); +int cpsw_miscintr(void *); + +void cpsw_get_mac_addr(struct cpsw_softc *); + +struct cfattach cpsw_ca = { + sizeof(struct cpsw_softc), + NULL, + cpsw_attach +}; + +struct cfdriver cpsw_cd = { + NULL, + "cpsw", + DV_IFNET +}; + +static inline u_int +cpsw_txdesc_adjust(u_int x, int y) +{ + return (((x) + y) & (CPSW_NTXDESCS - 1)); +} + +static inline u_int +cpsw_rxdesc_adjust(u_int x, int y) +{ + return (((x) + y) & (CPSW_NRXDESCS - 1)); +} + +static inline void +cpsw_set_txdesc_next(struct cpsw_softc * const sc, const u_int i, uint32_t n) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i + 0; + bus_space_write_4(sc->sc_bst, sc->sc_bsh_txdescs, o, n); +} + +static inline void +cpsw_set_rxdesc_next(struct cpsw_softc * const sc, const u_int i, uint32_t n) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i + 0; + bus_space_write_4(sc->sc_bst, sc->sc_bsh_rxdescs, o, n); +} + +static inline void +cpsw_get_txdesc(struct cpsw_softc * const sc, const u_int i, + struct cpsw_cpdma_bd * const bdp) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i; + bus_space_read_region_4(sc->sc_bst, sc->sc_bsh_txdescs, o, + (uint32_t *)bdp, 4); +} + +static inline void +cpsw_set_txdesc(struct cpsw_softc * const sc, const u_int i, + struct cpsw_cpdma_bd * const bdp) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i; + bus_space_write_region_4(sc->sc_bst, sc->sc_bsh_txdescs, o, + (uint32_t *)bdp, 4); +} + +static inline void +cpsw_get_rxdesc(struct cpsw_softc * const sc, const u_int i, + struct cpsw_cpdma_bd * const bdp) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i; + bus_space_read_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, o, + (uint32_t *)bdp, 4); +} + +static inline void +cpsw_set_rxdesc(struct cpsw_softc * const sc, const u_int i, + struct cpsw_cpdma_bd * const bdp) +{ + const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i; + bus_space_write_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, o, + (uint32_t *)bdp, 4); +} + +static inline bus_addr_t +cpsw_txdesc_paddr(struct cpsw_softc * const sc, u_int x) +{ + KASSERT(x < CPSW_NTXDESCS); + return sc->sc_txdescs_pa + sizeof(struct cpsw_cpdma_bd) * x; +} + +static inline bus_addr_t +cpsw_rxdesc_paddr(struct cpsw_softc * const sc, u_int x) +{ + KASSERT(x < CPSW_NRXDESCS); + return sc->sc_rxdescs_pa + sizeof(struct cpsw_cpdma_bd) * x; +} + +void +cpsw_get_mac_addr(struct cpsw_softc *sc) +{ + struct arpcom *ac = &sc->sc_ac; + u_int32_t mac_lo = 0, mac_hi = 0; + + sitara_cm_reg_read_4(OMAP2SCM_MAC_ID0_LO, &mac_lo); + sitara_cm_reg_read_4(OMAP2SCM_MAC_ID0_HI, &mac_hi); + + if ((mac_lo == 0) && (mac_hi == 0)) + printf("%s: invalid ethernet address\n", DEVNAME(sc)); + else { + ac->ac_enaddr[0] = (mac_hi >> 0) & 0xff; + ac->ac_enaddr[1] = (mac_hi >> 8) & 0xff; + ac->ac_enaddr[2] = (mac_hi >> 16) & 0xff; + ac->ac_enaddr[3] = (mac_hi >> 24) & 0xff; + ac->ac_enaddr[4] = (mac_lo >> 0) & 0xff; + ac->ac_enaddr[5] = (mac_lo >> 8) & 0xff; + } +} + +static void +cpsw_mdio_init(struct cpsw_softc *sc) +{ + uint32_t alive, link; + u_int tries; + + sc->sc_active_port = 0; + + /* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */ + /* TODO Calculate MDCLK=CLK/(CLKDIV+1) */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOCONTROL, + (1<<30) | (1<<18) | 0xFF); + + for(tries = 0; tries < 1000; tries++) { + alive = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOALIVE) & 3; + if (alive) + break; + delay(1); + } + + if (alive == 0) { + printf("%s: no PHY is alive\n", DEVNAME(sc)); + return; + } + + link = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOLINK) & 3; + + if (alive == 3) { + /* both ports are alive, prefer one with link */ + if (link == 2) + sc->sc_active_port = 1; + } else if (alive == 2) + sc->sc_active_port = 1; + + /* Select the port to monitor */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERPHYSEL0, + sc->sc_active_port); +} + +void +cpsw_attach(struct device *parent, struct device *self, void *aux) +{ + struct cpsw_softc *sc = (struct cpsw_softc *)self; + struct armv7_attach_args *aa = aux; + struct arpcom * const ac = &sc->sc_ac; + struct ifnet * const ifp = &ac->ac_if; + u_int32_t idver; + int error; + u_int i; + + timeout_set(&sc->sc_tick, cpsw_tick, sc); + + cpsw_get_mac_addr(sc); + + sc->sc_rxthih = arm_intr_establish(aa->aa_dev->irq[0] + + CPSW_INTROFF_RXTH, IPL_NET, cpsw_rxthintr, sc, DEVNAME(sc)); + sc->sc_rxih = arm_intr_establish(aa->aa_dev->irq[0] + + CPSW_INTROFF_RX, IPL_NET, cpsw_rxintr, sc, DEVNAME(sc)); + sc->sc_txih = arm_intr_establish(aa->aa_dev->irq[0] + + CPSW_INTROFF_TX, IPL_NET, cpsw_txintr, sc, DEVNAME(sc)); + sc->sc_miscih = arm_intr_establish(aa->aa_dev->irq[0] + + CPSW_INTROFF_MISC, IPL_NET, cpsw_miscintr, sc, DEVNAME(sc)); + + sc->sc_bst = aa->aa_iot; + sc->sc_bdt = aa->aa_dmat; + + error = bus_space_map(sc->sc_bst, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_bsh); + if (error) { + printf("can't map registers: %d\n", error); + return; + } + + sc->sc_txdescs_pa = aa->aa_dev->mem[0].addr + + CPSW_CPPI_RAM_TXDESCS_BASE; + error = bus_space_subregion(sc->sc_bst, sc->sc_bsh, + CPSW_CPPI_RAM_TXDESCS_BASE, CPSW_CPPI_RAM_TXDESCS_SIZE, + &sc->sc_bsh_txdescs); + if (error) { + printf("can't subregion tx ring SRAM: %d\n", error); + return; + } + + sc->sc_rxdescs_pa = aa->aa_dev->mem[0].addr + + CPSW_CPPI_RAM_RXDESCS_BASE; + error = bus_space_subregion(sc->sc_bst, sc->sc_bsh, + CPSW_CPPI_RAM_RXDESCS_BASE, CPSW_CPPI_RAM_RXDESCS_SIZE, + &sc->sc_bsh_rxdescs); + if (error) { + printf("can't subregion rx ring SRAM: %d\n", error); + return; + } + + sc->sc_rdp = malloc(sizeof(*sc->sc_rdp), M_TEMP, M_WAITOK); + KASSERT(sc->sc_rdp != NULL); + + for (i = 0; i < CPSW_NTXDESCS; i++) { + if ((error = bus_dmamap_create(sc->sc_bdt, MCLBYTES, + CPSW_TXFRAGS, MCLBYTES, 0, 0, + &sc->sc_rdp->tx_dm[i])) != 0) { + printf("unable to create tx DMA map: %d\n", error); + } + sc->sc_rdp->tx_mb[i] = NULL; + } + + for (i = 0; i < CPSW_NRXDESCS; i++) { + if ((error = bus_dmamap_create(sc->sc_bdt, MCLBYTES, 1, + MCLBYTES, 0, 0, &sc->sc_rdp->rx_dm[i])) != 0) { + printf("unable to create rx DMA map: %d\n", error); + } + sc->sc_rdp->rx_mb[i] = NULL; + } + + sc->sc_txpad = dma_alloc(ETHER_MIN_LEN, PR_WAITOK | PR_ZERO); + KASSERT(sc->sc_txpad != NULL); + bus_dmamap_create(sc->sc_bdt, ETHER_MIN_LEN, 1, ETHER_MIN_LEN, 0, + BUS_DMA_WAITOK, &sc->sc_txpad_dm); + bus_dmamap_load(sc->sc_bdt, sc->sc_txpad_dm, sc->sc_txpad, + ETHER_MIN_LEN, NULL, BUS_DMA_WAITOK|BUS_DMA_WRITE); + bus_dmamap_sync(sc->sc_bdt, sc->sc_txpad_dm, 0, ETHER_MIN_LEN, + BUS_DMASYNC_PREWRITE); + + idver = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_IDVER); + printf(": version %d.%d (%d), address %s\n", + CPSW_SS_IDVER_MAJ(idver), CPSW_SS_IDVER_MIN(idver), + CPSW_SS_IDVER_RTL(idver), ether_sprintf(ac->ac_enaddr)); + + ifp->if_softc = sc; + ifp->if_capabilities = 0; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = cpsw_start; + ifp->if_ioctl = cpsw_ioctl; + ifp->if_watchdog = cpsw_watchdog; + IFQ_SET_MAXLEN(&ifp->if_snd, CPSW_NTXDESCS - 1); + memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); + + cpsw_stop(ifp); + + sc->sc_mii.mii_ifp = ifp; + sc->sc_mii.mii_readreg = cpsw_mii_readreg; + sc->sc_mii.mii_writereg = cpsw_mii_writereg; + sc->sc_mii.mii_statchg = cpsw_mii_statchg; + + cpsw_mdio_init(sc); + + ifmedia_init(&sc->sc_mii.mii_media, 0, cpsw_mediachange, + cpsw_mediastatus); + mii_attach(self, &sc->sc_mii, 0xffffffff, + MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + printf("no PHY found!\n"); + ifmedia_add(&sc->sc_mii.mii_media, + IFM_ETHER|IFM_MANUAL, 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL); + } else { + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); + } + + if_attach(ifp); + ether_ifattach(ifp); + + return; +} + +int +cpsw_mediachange(struct ifnet *ifp) +{ + struct cpsw_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) + mii_mediachg(&sc->sc_mii); + + return (0); +} + +void +cpsw_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct cpsw_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) { + mii_pollstat(&sc->sc_mii); + ifmr->ifm_active = sc->sc_mii.mii_media_active; + ifmr->ifm_status = sc->sc_mii.mii_media_status; + } +} + +void +cpsw_start(struct ifnet *ifp) +{ + struct cpsw_softc * const sc = ifp->if_softc; + struct cpsw_ring_data * const rdp = sc->sc_rdp; + struct cpsw_cpdma_bd bd; + struct mbuf *m; + bus_dmamap_t dm; + u_int eopi = ~0; + u_int seg; + u_int txfree; + int txstart = -1; + int error; + bool pad; + u_int mlen; + + if (!ISSET(ifp->if_flags, IFF_RUNNING) || + ifq_is_oactive(&ifp->if_snd) || + IFQ_IS_EMPTY(&ifp->if_snd)) + return; + + if (sc->sc_txnext >= sc->sc_txhead) + txfree = CPSW_NTXDESCS - 1 + sc->sc_txhead - sc->sc_txnext; + else + txfree = sc->sc_txhead - sc->sc_txnext - 1; + + for (;;) { + if (txfree <= CPSW_TXFRAGS) { + ifq_set_oactive(&ifp->if_snd); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + dm = rdp->tx_dm[sc->sc_txnext]; + error = bus_dmamap_load_mbuf(sc->sc_bdt, dm, m, BUS_DMA_NOWAIT); + switch (error) { + case 0: + break; + + case EFBIG: /* mbuf chain is too fragmented */ + if (m_defrag(m, M_DONTWAIT) == 0 && + bus_dmamap_load_mbuf(sc->sc_bdt, dm, m, + BUS_DMA_NOWAIT) == 0) + break; + + /* FALLTHROUGH */ + default: + m_freem(m); + ifp->if_oerrors++; + continue; + } + + mlen = dm->dm_mapsize; + pad = mlen < CPSW_PAD_LEN; + + KASSERT(rdp->tx_mb[sc->sc_txnext] == NULL); + rdp->tx_mb[sc->sc_txnext] = m; + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#endif + + bus_dmamap_sync(sc->sc_bdt, dm, 0, dm->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + if (txstart == -1) + txstart = sc->sc_txnext; + eopi = sc->sc_txnext; + for (seg = 0; seg < dm->dm_nsegs; seg++) { + bd.next = cpsw_txdesc_paddr(sc, + TXDESC_NEXT(sc->sc_txnext)); + bd.bufptr = dm->dm_segs[seg].ds_addr; + bd.bufoff = 0; + bd.buflen = dm->dm_segs[seg].ds_len; + bd.pktlen = 0; + bd.flags = 0; + + if (seg == 0) { + bd.flags = CPDMA_BD_OWNER | CPDMA_BD_SOP; + bd.pktlen = MAX(mlen, CPSW_PAD_LEN); + } + + if (seg == dm->dm_nsegs - 1 && !pad) + bd.flags |= CPDMA_BD_EOP; + + cpsw_set_txdesc(sc, sc->sc_txnext, &bd); + txfree--; + eopi = sc->sc_txnext; + sc->sc_txnext = TXDESC_NEXT(sc->sc_txnext); + } + if (pad) { + bd.next = cpsw_txdesc_paddr(sc, + TXDESC_NEXT(sc->sc_txnext)); + bd.bufptr = sc->sc_txpad_pa; + bd.bufoff = 0; + bd.buflen = CPSW_PAD_LEN - mlen; + bd.pktlen = 0; + bd.flags = CPDMA_BD_EOP; + + cpsw_set_txdesc(sc, sc->sc_txnext, &bd); + txfree--; + eopi = sc->sc_txnext; + sc->sc_txnext = TXDESC_NEXT(sc->sc_txnext); + } + } + + if (txstart >= 0) { + ifp->if_timer = 5; + /* terminate the new chain */ + KASSERT(eopi == TXDESC_PREV(sc->sc_txnext)); + cpsw_set_txdesc_next(sc, TXDESC_PREV(sc->sc_txnext), 0); + + /* link the new chain on */ + cpsw_set_txdesc_next(sc, TXDESC_PREV(txstart), + cpsw_txdesc_paddr(sc, txstart)); + if (sc->sc_txeoq) { + /* kick the dma engine */ + sc->sc_txeoq = false; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0), + cpsw_txdesc_paddr(sc, txstart)); + } + } +} + +int +cpsw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct cpsw_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int s = splnet(); + int error = 0; + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + cpsw_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + cpsw_stop(ifp); + } + break; + case SIOCSIFMEDIA: + ifr->ifr_media &= ~IFM_ETH_FMASK; + /* FALLTHROUGH */ + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + cpsw_init(ifp); + error = 0; + } + + splx(s); + + return error; +} + +void +cpsw_watchdog(struct ifnet *ifp) +{ + printf("%s: device timeout\n", ifp->if_xname); + + ifp->if_oerrors++; + cpsw_init(ifp); + cpsw_start(ifp); +} + +static int +cpsw_mii_wait(struct cpsw_softc * const sc, int reg) +{ + u_int tries; + + for(tries = 0; tries < 1000; tries++) { + if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg) & (1U << 31)) == 0) + return 0; + delay(1); + } + return ETIMEDOUT; +} + +int +cpsw_mii_readreg(struct device *dev, int phy, int reg) +{ + struct cpsw_softc * const sc = (struct cpsw_softc *)dev; + uint32_t v; + + if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0) + return 0; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0, (1U << 31) | + ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16)); + + if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0) + return 0; + + v = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0); + if (v & (1 << 29)) + return v & 0xffff; + else + return 0; +} + +void +cpsw_mii_writereg(struct device *dev, int phy, int reg, int val) +{ + struct cpsw_softc * const sc = (struct cpsw_softc *)dev; + uint32_t v; + + KASSERT((val & 0xffff0000UL) == 0); + + if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0) + goto out; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0, (1U << 31) | (1 << 30) | + ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16) | val); + + if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0) + goto out; + + v = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0); + if ((v & (1 << 29)) == 0) +out: + printf("%s error\n", __func__); + +} + +void +cpsw_mii_statchg(struct device *self) +{ + return; +} + +int +cpsw_new_rxbuf(struct cpsw_softc * const sc, const u_int i) +{ + struct cpsw_ring_data * const rdp = sc->sc_rdp; + const u_int h = RXDESC_PREV(i); + struct cpsw_cpdma_bd bd; + struct mbuf *m; + int error = ENOBUFS; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + goto reuse; + } + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + goto reuse; + } + + /* We have a new buffer, prepare it for the ring. */ + + if (rdp->rx_mb[i] != NULL) + bus_dmamap_unload(sc->sc_bdt, rdp->rx_dm[i]); + + m->m_len = m->m_pkthdr.len = MCLBYTES; + + rdp->rx_mb[i] = m; + + error = bus_dmamap_load_mbuf(sc->sc_bdt, rdp->rx_dm[i], rdp->rx_mb[i], + BUS_DMA_READ|BUS_DMA_NOWAIT); + if (error) { + printf("can't load rx DMA map %d: %d\n", i, error); + } + + bus_dmamap_sync(sc->sc_bdt, rdp->rx_dm[i], + 0, rdp->rx_dm[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + + error = 0; + +reuse: + /* (re-)setup the descriptor */ + bd.next = 0; + bd.bufptr = rdp->rx_dm[i]->dm_segs[0].ds_addr; + bd.bufoff = 0; + bd.buflen = MIN(0x7ff, rdp->rx_dm[i]->dm_segs[0].ds_len); + bd.pktlen = 0; + bd.flags = CPDMA_BD_OWNER; + + cpsw_set_rxdesc(sc, i, &bd); + /* and link onto ring */ + cpsw_set_rxdesc_next(sc, h, cpsw_rxdesc_paddr(sc, i)); + + return error; +} + +int +cpsw_init(struct ifnet *ifp) +{ + struct cpsw_softc * const sc = ifp->if_softc; + struct arpcom *ac = &sc->sc_ac; + struct mii_data * const mii = &sc->sc_mii; + int i; + + cpsw_stop(ifp); + + sc->sc_txnext = 0; + sc->sc_txhead = 0; + + /* Reset wrapper */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET) & 1); + + /* Reset SS */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET) & 1); + + /* Clear table (30) and enable ALE(31) and set passthrough (4) */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_CONTROL, (3 << 30) | 0x10); + + /* Reset and init Sliver port 1 and 2 */ + for (i = 0; i < 2; i++) { + /* Reset */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i), 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i)) & 1); + /* Set Slave Mapping */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_RX_PRI_MAP(i), 0x76543210); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_TX_PRI_MAP(i+1), 0x33221100); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_RX_MAXLEN(i), 0x5f2); + /* Set MAC Address */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_SA_HI(i+1), + ac->ac_enaddr[0] | (ac->ac_enaddr[1] << 8) | + (ac->ac_enaddr[2] << 16) | (ac->ac_enaddr[3] << 24)); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_SA_LO(i+1), + ac->ac_enaddr[4] | (ac->ac_enaddr[5] << 8)); + + /* Set MACCONTROL for ports 0,1: FULLDUPLEX(0), GMII_EN(5), + IFCTL_A(15), IFCTL_B(16) FIXME */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_MACCONTROL(i), + 1 | (1<<5) | (1<<15) | (1<<16)); + + /* Set ALE port to forwarding(3) on the active port */ + if (i == sc->sc_active_port) + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_PORTCTL(i+1), 3); + } + + /* Set Host Port Mapping */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0); + + /* Set ALE port to forwarding(3) */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_PORTCTL(0), 3); + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_PTYPE, 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_STAT_PORT_EN, 7); + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET) & 1); + + for (i = 0; i < 8; i++) { + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(i), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(i), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(i), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(i), 0); + } + + bus_space_set_region_4(sc->sc_bst, sc->sc_bsh_txdescs, 0, 0, + CPSW_CPPI_RAM_TXDESCS_SIZE/4); + + sc->sc_txhead = 0; + sc->sc_txnext = 0; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_FREEBUFFER(0), 0); + + bus_space_set_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, 0, 0, + CPSW_CPPI_RAM_RXDESCS_SIZE/4); + + /* Initialize RX Buffer Descriptors */ + cpsw_set_rxdesc_next(sc, RXDESC_PREV(0), 0); + for (i = 0; i < CPSW_NRXDESCS; i++) { + cpsw_new_rxbuf(sc, i); + } + sc->sc_rxhead = 0; + + /* align layer 3 header to 32-bit */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_BUFFER_OFFSET, ETHER_ALIGN); + + /* Clear all interrupt Masks */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF); + + /* Enable TX & RX DMA */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CONTROL, 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CONTROL, 1); + + /* Enable interrupt pacing for C0 RX/TX (IMAX set to max intr/ms allowed) */ +#define CPSW_VBUSP_CLK_MHZ 2400 /* hardcoded for BBB */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_IMAX(0), 2); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_IMAX(0), 2); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_INT_CONTROL, 3 << 16 | CPSW_VBUSP_CLK_MHZ/4); + + /* Enable TX and RX interrupt receive for core 0 */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_EN(0), 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_EN(0), 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_EN(0), 0x1F); + + /* Enable host Error Interrupt */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTMASK_SET, 2); + + /* Enable interrupts for TX and RX Channel 0 */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_SET, 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_SET, 1); + + /* Ack stalled irqs */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RXTH); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RX); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_TX); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_MISC); + + cpsw_mdio_init(sc); + + mii_mediachg(mii); + + /* Write channel 0 RX HDP */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0), cpsw_rxdesc_paddr(sc, 0)); + sc->sc_rxrun = true; + sc->sc_rxeoq = false; + + sc->sc_txrun = true; + sc->sc_txeoq = true; + + ifp->if_flags |= IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + + timeout_add_sec(&sc->sc_tick, 1); + + return 0; +} + +void +cpsw_stop(struct ifnet *ifp) +{ + struct cpsw_softc * const sc = ifp->if_softc; + struct cpsw_ring_data * const rdp = sc->sc_rdp; + u_int i; + +#if 0 + /* XXX find where disable comes from */ + printf("%s: ifp %p disable %d\n", __func__, ifp, disable); +#endif + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + timeout_del(&sc->sc_tick); + + mii_down(&sc->sc_mii); + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_CLEAR, 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_CLEAR, 1); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_EN(0), 0x0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_EN(0), 0x0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_EN(0), 0x1F); + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_TEARDOWN, 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_TEARDOWN, 0); + i = 0; + while ((sc->sc_txrun || sc->sc_rxrun) && i < 10000) { + delay(10); + if ((sc->sc_txrun == true) && cpsw_txintr(sc) == 0) + sc->sc_txrun = false; + if ((sc->sc_rxrun == true) && cpsw_rxintr(sc) == 0) + sc->sc_rxrun = false; + i++; + } + /* printf("%s toredown complete in %u\n", __func__, i); */ + + /* Reset wrapper */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET) & 1); + + /* Reset SS */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET) & 1); + + for (i = 0; i < 2; i++) { + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i), 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i)) & 1); + } + + /* Reset CPDMA */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET, 1); + while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET) & 1); + + /* Release any queued transmit buffers. */ + for (i = 0; i < CPSW_NTXDESCS; i++) { + bus_dmamap_unload(sc->sc_bdt, rdp->tx_dm[i]); + m_freem(rdp->tx_mb[i]); + rdp->tx_mb[i] = NULL; + } + + ifp->if_flags &= ~IFF_RUNNING; + ifp->if_timer = 0; + ifq_clr_oactive(&ifp->if_snd); + + /* XXX Not sure what this is doing calling disable here + where is disable set? + */ +#if 0 + if (!disable) + return; +#endif + + for (i = 0; i < CPSW_NRXDESCS; i++) { + bus_dmamap_unload(sc->sc_bdt, rdp->rx_dm[i]); + m_freem(rdp->rx_mb[i]); + rdp->rx_mb[i] = NULL; + } +} + +int +cpsw_rxthintr(void *arg) +{ + struct cpsw_softc * const sc = arg; + + /* this won't deassert the interrupt though */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RXTH); + + return 1; +} + +int +cpsw_rxintr(void *arg) +{ + struct cpsw_softc * const sc = arg; + struct ifnet * const ifp = &sc->sc_ac.ac_if; + struct cpsw_ring_data * const rdp = sc->sc_rdp; + struct cpsw_cpdma_bd bd; + bus_dmamap_t dm; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct mbuf *m; + u_int i; + u_int len, off; + + sc->sc_rxeoq = false; + + for (;;) { + KASSERT(sc->sc_rxhead < CPSW_NRXDESCS); + + i = sc->sc_rxhead; + dm = rdp->rx_dm[i]; + m = rdp->rx_mb[i]; + + KASSERT(dm != NULL); + KASSERT(m != NULL); + + cpsw_get_rxdesc(sc, i, &bd); + + if (bd.flags & CPDMA_BD_OWNER) + break; + + if (bd.flags & CPDMA_BD_TDOWNCMPLT) { + sc->sc_rxrun = false; + goto done; + } + + bus_dmamap_sync(sc->sc_bdt, dm, 0, dm->dm_mapsize, + BUS_DMASYNC_POSTREAD); + + if (cpsw_new_rxbuf(sc, i) != 0) { + /* drop current packet, reuse buffer for new */ + ifp->if_ierrors++; + goto next; + } + + if ((bd.flags & (CPDMA_BD_SOP|CPDMA_BD_EOP)) != + (CPDMA_BD_SOP|CPDMA_BD_EOP)) { + if (bd.flags & CPDMA_BD_SOP) { + printf("cpsw: rx packet too large\n"); + ifp->if_ierrors++; + } + m_freem(m); + goto next; + } + + off = bd.bufoff; + len = bd.pktlen; + + if (bd.flags & CPDMA_BD_PASSCRC) + len -= ETHER_CRC_LEN; + + m->m_pkthdr.len = m->m_len = len; + m->m_data += off; + + ml_enqueue(&ml, m); + +next: + sc->sc_rxhead = RXDESC_NEXT(sc->sc_rxhead); + if (bd.flags & CPDMA_BD_EOQ) { + sc->sc_rxeoq = true; + sc->sc_rxrun = false; + } + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(0), + cpsw_rxdesc_paddr(sc, i)); + } + + if (sc->sc_rxeoq) { + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0), + cpsw_rxdesc_paddr(sc, sc->sc_rxhead)); + sc->sc_rxrun = true; + sc->sc_rxeoq = false; + } + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, + CPSW_INTROFF_RX); + +done: + if_input(ifp, &ml); + + return 1; +} + +void +cpsw_tick(void *arg) +{ + struct cpsw_softc *sc = arg; + int s; + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + timeout_add_sec(&sc->sc_tick, 1); +} + +int +cpsw_txintr(void *arg) +{ + struct cpsw_softc * const sc = arg; + struct ifnet * const ifp = &sc->sc_ac.ac_if; + struct cpsw_ring_data * const rdp = sc->sc_rdp; + struct cpsw_cpdma_bd bd; + bool handled = false; + uint32_t tx0_cp; + u_int cpi; + + KASSERT(sc->sc_txrun); + + tx0_cp = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0)); + + if (tx0_cp == 0xfffffffc) { + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0), 0xfffffffc); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0), 0); + sc->sc_txrun = false; + return 0; + } + + for (;;) { + tx0_cp = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0)); + cpi = (tx0_cp - sc->sc_txdescs_pa) / + sizeof(struct cpsw_cpdma_bd); + KASSERT(sc->sc_txhead < CPSW_NTXDESCS); + + cpsw_get_txdesc(sc, sc->sc_txhead, &bd); + + if (bd.buflen == 0) { + /* Debugger(); */ + } + + if ((bd.flags & CPDMA_BD_SOP) == 0) + goto next; + + if (bd.flags & CPDMA_BD_OWNER) { + printf("pwned %x %x %x\n", cpi, sc->sc_txhead, + sc->sc_txnext); + break; + } + + if (bd.flags & CPDMA_BD_TDOWNCMPLT) { + sc->sc_txrun = false; + return 1; + } + + bus_dmamap_sync(sc->sc_bdt, rdp->tx_dm[sc->sc_txhead], + 0, rdp->tx_dm[sc->sc_txhead]->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_bdt, rdp->tx_dm[sc->sc_txhead]); + + m_freem(rdp->tx_mb[sc->sc_txhead]); + rdp->tx_mb[sc->sc_txhead] = NULL; + + ifp->if_opackets++; + + handled = true; + + ifq_clr_oactive(&ifp->if_snd); + +next: + if ((bd.flags & (CPDMA_BD_EOP|CPDMA_BD_EOQ)) == + (CPDMA_BD_EOP|CPDMA_BD_EOQ)) + sc->sc_txeoq = true; + + if (sc->sc_txhead == cpi) { + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0), + cpsw_txdesc_paddr(sc, cpi)); + sc->sc_txhead = TXDESC_NEXT(sc->sc_txhead); + break; + } + sc->sc_txhead = TXDESC_NEXT(sc->sc_txhead); + if (sc->sc_txeoq == true) + break; + } + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_TX); + + if ((sc->sc_txnext != sc->sc_txhead) && sc->sc_txeoq) { + if (bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0)) == 0) { + sc->sc_txeoq = false; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0), + cpsw_txdesc_paddr(sc, sc->sc_txhead)); + } + } + + if (handled && sc->sc_txnext == sc->sc_txhead) + ifp->if_timer = 0; + + if (handled) + cpsw_start(ifp); + + return handled; +} + +int +cpsw_miscintr(void *arg) +{ + struct cpsw_softc * const sc = arg; + uint32_t miscstat; + uint32_t dmastat; + uint32_t stat; + + miscstat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_STAT(0)); + printf("%s %x FIRE\n", __func__, miscstat); + + if (miscstat & CPSW_MISC_HOST_PEND) { + /* Host Error */ + dmastat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTSTAT_MASKED); + printf("CPSW_CPDMA_DMA_INTSTAT_MASKED %x\n", dmastat); + + printf("rxhead %02x\n", sc->sc_rxhead); + + stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMASTATUS); + printf("CPSW_CPDMA_DMASTATUS %x\n", stat); + stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0)); + printf("CPSW_CPDMA_TX0_HDP %x\n", stat); + stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0)); + printf("CPSW_CPDMA_TX0_CP %x\n", stat); + stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0)); + printf("CPSW_CPDMA_RX0_HDP %x\n", stat); + stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(0)); + printf("CPSW_CPDMA_RX0_CP %x\n", stat); + + /* Debugger(); */ + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTMASK_CLEAR, dmastat); + dmastat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTSTAT_MASKED); + printf("CPSW_CPDMA_DMA_INTSTAT_MASKED %x\n", dmastat); + } + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_MISC); + + return 1; +} diff --git a/if_cpswreg.h b/if_cpswreg.h new file mode 100644 index 0000000..98ff229 --- /dev/null +++ b/if_cpswreg.h @@ -0,0 +1,146 @@ +/* $OpenBSD: if_cpswreg.h,v 1.6 2016/03/02 01:31:41 canacar Exp $ */ + +/*- + * 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 _IF_CPSWREG_H +#define _IF_CPSWREG_H + +#define CPSW_SS_OFFSET 0x0000 +#define CPSW_SS_IDVER (CPSW_SS_OFFSET + 0x00) +#define CPSW_SS_IDVER_RTL(_r) (((_r) >> 11) & 0x1f) +#define CPSW_SS_IDVER_MAJ(_r) (((_r) >> 8) & 0x7) +#define CPSW_SS_IDVER_MIN(_r) ((_r) & 0xff) +#define CPSW_SS_SOFT_RESET (CPSW_SS_OFFSET + 0x08) +#define CPSW_SS_STAT_PORT_EN (CPSW_SS_OFFSET + 0x0C) +#define CPSW_SS_PTYPE (CPSW_SS_OFFSET + 0x10) + +#define CPSW_PORT_OFFSET 0x0100 +#define CPSW_PORT_P_TX_PRI_MAP(p) (CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100)) +#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP (CPSW_PORT_OFFSET + 0x01C) +#define CPSW_PORT_P0_CPDMA_RX_CH_MAP (CPSW_PORT_OFFSET + 0x020) +#define CPSW_PORT_P_SA_LO(p) (CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100)) +#define CPSW_PORT_P_SA_HI(p) (CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100)) + +#define CPSW_CPDMA_OFFSET 0x0800 +#define CPSW_CPDMA_TX_CONTROL (CPSW_CPDMA_OFFSET + 0x04) +#define CPSW_CPDMA_TX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x08) +#define CPSW_CPDMA_RX_CONTROL (CPSW_CPDMA_OFFSET + 0x14) +#define CPSW_CPDMA_RX_TEARDOWN (CPSW_CPDMA_OFFSET + 0x18) +#define CPSW_CPDMA_SOFT_RESET (CPSW_CPDMA_OFFSET + 0x1c) +#define CPSW_CPDMA_DMACONTROL (CPSW_CPDMA_OFFSET + 0x20) +#define CPSW_CPDMA_DMASTATUS (CPSW_CPDMA_OFFSET + 0x24) +#define CPSW_CPDMA_RX_BUFFER_OFFSET (CPSW_CPDMA_OFFSET + 0x28) +#define CPSW_CPDMA_TX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0x80) +#define CPSW_CPDMA_TX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0x84) +#define CPSW_CPDMA_TX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0x88) +#define CPSW_CPDMA_TX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0x8C) +#define CPSW_CPDMA_CPDMA_EOI_VECTOR (CPSW_CPDMA_OFFSET + 0x94) +#define CPSW_CPDMA_RX_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xA0) +#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4) +#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8) +#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc) +#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0) +#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4) +#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8) +#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC) +#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04)) + +#define CPSW_STATS_OFFSET 0x0900 + +#define CPSW_STATERAM_OFFSET 0x0A00 +#define CPSW_CPDMA_TX_HDP(p) (CPSW_STATERAM_OFFSET + 0x00 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_HDP(p) (CPSW_STATERAM_OFFSET + 0x20 + ((p) * 0x04)) +#define CPSW_CPDMA_TX_CP(p) (CPSW_STATERAM_OFFSET + 0x40 + ((p) * 0x04)) +#define CPSW_CPDMA_RX_CP(p) (CPSW_STATERAM_OFFSET + 0x60 + ((p) * 0x04)) + +#define CPSW_CPTS_OFFSET 0x0C00 + +#define CPSW_ALE_OFFSET 0x0D00 +#define CPSW_ALE_CONTROL (CPSW_ALE_OFFSET + 0x08) +#define CPSW_ALE_TBLCTL (CPSW_ALE_OFFSET + 0x20) +#define CPSW_ALE_TBLW2 (CPSW_ALE_OFFSET + 0x34) +#define CPSW_ALE_TBLW1 (CPSW_ALE_OFFSET + 0x38) +#define CPSW_ALE_TBLW0 (CPSW_ALE_OFFSET + 0x3C) +#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04)) + +#define CPSW_SL_OFFSET 0x0D80 +#define CPSW_SL_MACCONTROL(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x04) +#define CPSW_SL_SOFT_RESET(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C) +#define CPSW_SL_RX_MAXLEN(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x10) +#define CPSW_SL_RX_PRI_MAP(p) (CPSW_SL_OFFSET + (0x40 * (p)) + 0x24) + +#define MDIO_OFFSET 0x1000 +#define MDIOCONTROL (MDIO_OFFSET + 0x04) +#define MDIOALIVE (MDIO_OFFSET + 0x08) +#define MDIOLINK (MDIO_OFFSET + 0x0C) +#define MDIOUSERACCESS0 (MDIO_OFFSET + 0x80) +#define MDIOUSERPHYSEL0 (MDIO_OFFSET + 0x84) + +#define CPSW_WR_OFFSET 0x1200 +#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04) +#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08) +#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c) +#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10) +#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14) +#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18) +#define CPSW_WR_C_MISC_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C) +#define CPSW_WR_C_RX_THRESH_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x40) +#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44) +#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48) +#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C) +#define CPSW_WR_C_RX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x70) +#define CPSW_WR_C_TX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x74) +#define CPSW_MISC_HOST_PEND 0x0004 + +#define CPSW_CPPI_RAM_OFFSET 0x2000 + +#define CPDMA_BD_SOP (1<<15) +#define CPDMA_BD_EOP (1<<14) +#define CPDMA_BD_OWNER (1<<13) +#define CPDMA_BD_EOQ (1<<12) +#define CPDMA_BD_TDOWNCMPLT (1<<11) +#define CPDMA_BD_PASSCRC (1<<10) +#define CPDMA_BD_PKT_ERR_MASK (3<< 4) + +struct cpsw_cpdma_bd { + uint32_t next; + uint32_t bufptr; + uint16_t buflen; + uint16_t bufoff; + uint16_t pktlen; + uint16_t flags; +}; + +/* Interrupt offsets */ +#define CPSW_INTROFF_RXTH 0 +#define CPSW_INTROFF_RX 1 +#define CPSW_INTROFF_TX 2 +#define CPSW_INTROFF_MISC 3 + +#endif /*_IF_CPSWREG_H */ diff --git a/intc.c b/intc.c new file mode 100644 index 0000000..335f9e6 --- /dev/null +++ b/intc.c @@ -0,0 +1,423 @@ +/* $OpenBSD: intc.c,v 1.4 2016/01/31 00:14:50 jsg Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "intc.h" + +#define INTC_NUM_IRQ intc_nirq +#define INTC_NUM_BANKS (intc_nirq/32) +#define INTC_MAX_IRQ 128 +#define INTC_MAX_BANKS (INTC_MAX_IRQ/32) + +/* registers */ +#define INTC_REVISION 0x00 /* R */ +#define INTC_SYSCONFIG 0x10 /* RW */ +#define INTC_SYSCONFIG_AUTOIDLE 0x1 +#define INTC_SYSCONFIG_SOFTRESET 0x2 +#define INTC_SYSSTATUS 0x14 /* R */ +#define INTC_SYSSYSTATUS_RESETDONE 0x1 +#define INTC_SIR_IRQ 0x40 /* R */ +#define INTC_SIR_FIQ 0x44 /* R */ +#define INTC_CONTROL 0x48 /* RW */ +#define INTC_CONTROL_NEWIRQ 0x1 +#define INTC_CONTROL_NEWFIQ 0x2 +#define INTC_CONTROL_GLOBALMASK 0x1 +#define INTC_PROTECTION 0x4c /* RW */ +#define INTC_PROTECTION_PROT 1 /* only privileged mode */ +#define INTC_IDLE 0x50 /* RW */ + +#define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3) +#define INTC_IRQ_TO_REGi(i) ((i) & 0x1f) +#define INTC_ITRn(i) 0x80+(0x20*i)+0x00 /* R */ +#define INTC_MIRn(i) 0x80+(0x20*i)+0x04 /* RW */ +#define INTC_CLEARn(i) 0x80+(0x20*i)+0x08 /* RW */ +#define INTC_SETn(i) 0x80+(0x20*i)+0x0c /* RW */ +#define INTC_ISR_SETn(i) 0x80+(0x20*i)+0x10 /* RW */ +#define INTC_ISR_CLEARn(i) 0x80+(0x20*i)+0x14 /* RW */ +#define INTC_PENDING_IRQn(i) 0x80+(0x20*i)+0x18 /* R */ +#define INTC_PENDING_FIQn(i) 0x80+(0x20*i)+0x1c /* R */ + +#define INTC_ILRn(i) 0x100+(4*i) +#define INTC_ILR_IRQ 0x0 /* not of FIQ */ +#define INTC_ILR_FIQ 0x1 +#define INTC_ILR_PRIs(pri) ((pri) << 2) +#define INTC_ILR_PRI(reg) (((reg) >> 2) & 0x2f) +#define INTC_MIN_PRI 63 +#define INTC_STD_PRI 32 +#define INTC_MAX_PRI 0 + +struct intrhand { + TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ + int (*ih_func)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_ipl; /* IPL_* */ + int ih_irq; /* IRQ number */ + struct evcount ih_count; + char *ih_name; +}; + +struct intrq { + TAILQ_HEAD(, intrhand) iq_list; /* handler list */ + int iq_irq; /* IRQ to mask while handling */ + int iq_levels; /* IPL_*'s this IRQ has */ + int iq_ist; /* share type */ +}; + +volatile int softint_pending; + +struct intrq intc_handler[INTC_MAX_IRQ]; +u_int32_t intc_smask[NIPL]; +u_int32_t intc_imask[INTC_MAX_BANKS][NIPL]; + +bus_space_tag_t intc_iot; +bus_space_handle_t intc_ioh; +int intc_nirq; + +void intc_attach(struct device *, struct device *, void *); +int intc_spllower(int new); +int intc_splraise(int new); +void intc_setipl(int new); +void intc_calc_mask(void); + +struct cfattach intc_ca = { + sizeof (struct device), NULL, intc_attach +}; + +struct cfdriver intc_cd = { + NULL, "intc", DV_DULL +}; + +int intc_attached = 0; + +void +intc_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + int i; + u_int32_t rev; + + intc_iot = aa->aa_iot; + if (bus_space_map(intc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &intc_ioh)) + panic("intc_attach: bus_space_map failed!"); + + rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION); + + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + + /* software reset of the part? */ + /* set protection bit (kernel only)? */ +#if 0 + bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION, + INTC_PROTECTION_PROT); +#endif + + /* enable interface clock power saving mode */ + bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG, + INTC_SYSCONFIG_AUTOIDLE); + + switch (board_id) { + case BOARD_ID_AM335X_BEAGLEBONE: + intc_nirq = 128; + break; + default: + intc_nirq = 96; + break; + } + + /* mask all interrupts */ + for (i = 0; i < INTC_NUM_BANKS; i++) + bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff); + + for (i = 0; i < INTC_NUM_IRQ; i++) { + bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i), + INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ); + + TAILQ_INIT(&intc_handler[i].iq_list); + } + + intc_calc_mask(); + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + + intc_attached = 1; + + /* insert self as interrupt handler */ + arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx, + intc_setipl, + intc_intr_establish, intc_intr_disestablish, intc_intr_string, + intc_irq_handler); + + intc_setipl(IPL_HIGH); /* XXX ??? */ + enable_interrupts(PSR_I); +} + +void +intc_calc_mask(void) +{ + struct cpu_info *ci = curcpu(); + int irq; + struct intrhand *ih; + int i; + + for (irq = 0; irq < INTC_NUM_IRQ; irq++) { + int max = IPL_NONE; + int min = IPL_HIGH; + TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { + if (ih->ih_ipl > max) + max = ih->ih_ipl; + + if (ih->ih_ipl < min) + min = ih->ih_ipl; + } + + intc_handler[irq].iq_irq = max; + + if (max == IPL_NONE) + min = IPL_NONE; + +#ifdef DEBUG_INTC + if (min != IPL_NONE) { + printf("irq %d to block at %d %d reg %d bit %d\n", + irq, max, min, INTC_IRQ_TO_REG(irq), + INTC_IRQ_TO_REGi(irq)); + } +#endif + /* Enable interrupts at lower levels, clear -> enable */ + for (i = 0; i < min; i++) + intc_imask[INTC_IRQ_TO_REG(irq)][i] &= + ~(1 << INTC_IRQ_TO_REGi(irq)); + for (; i <= IPL_HIGH; i++) + intc_imask[INTC_IRQ_TO_REG(irq)][i] |= + 1 << INTC_IRQ_TO_REGi(irq); + /* XXX - set enable/disable, priority */ + bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq), + INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ); + } + arm_init_smask(); + intc_setipl(ci->ci_cpl); +} + +void +intc_splx(int new) +{ + struct cpu_info *ci = curcpu(); + intc_setipl(new); + + if (ci->ci_ipending & arm_smask[ci->ci_cpl]) + arm_do_pending_intr(ci->ci_cpl); +} + +int +intc_spllower(int new) +{ + struct cpu_info *ci = curcpu(); + int old = ci->ci_cpl; + intc_splx(new); + return (old); +} + +int +intc_splraise(int new) +{ + struct cpu_info *ci = curcpu(); + int old; + old = ci->ci_cpl; + + /* + * setipl must always be called because there is a race window + * where the variable is updated before the mask is set + * an interrupt occurs in that window without the mask always + * being set, the hardware might not get updated on the next + * splraise completely messing up spl protection. + */ + if (old > new) + new = old; + + intc_setipl(new); + + return (old); +} + +void +intc_setipl(int new) +{ + struct cpu_info *ci = curcpu(); + int i; + int psw; + if (intc_attached == 0) + return; + + psw = disable_interrupts(PSR_I); +#if 0 + { + volatile static int recursed = 0; + if (recursed == 0) { + recursed = 1; + if (new != 12) + printf("setipl %d\n", new); + recursed = 0; + } + } +#endif + ci->ci_cpl = new; + for (i = 0; i < INTC_NUM_BANKS; i++) + bus_space_write_4(intc_iot, intc_ioh, + INTC_MIRn(i), intc_imask[i][new]); + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + restore_interrupts(psw); +} + +void +intc_intr_bootstrap(vaddr_t addr) +{ + int i, j; + extern struct bus_space armv7_bs_tag; + intc_iot = &armv7_bs_tag; + intc_ioh = addr; + for (i = 0; i < INTC_NUM_BANKS; i++) + for (j = 0; j < NIPL; j++) + intc_imask[i][j] = 0xffffffff; +} + +void +intc_irq_handler(void *frame) +{ + int irq, pri, s; + struct intrhand *ih; + void *arg; + + irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ); +#ifdef DEBUG_INTC + printf("irq %d fired\n", irq); +#endif + + pri = intc_handler[irq].iq_irq; + s = intc_splraise(pri); + TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { + if (ih->ih_arg != 0) + arg = ih->ih_arg; + else + arg = frame; + + if (ih->ih_func(arg)) + ih->ih_count.ec_count++; + + } + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + + intc_splx(s); +} + +void * +intc_intr_establish(int irqno, int level, int (*func)(void *), + void *arg, char *name) +{ + int psw; + struct intrhand *ih; + + if (irqno < 0 || irqno >= INTC_NUM_IRQ) + panic("intc_intr_establish: bogus irqnumber %d: %s", + irqno, name); + psw = disable_interrupts(PSR_I); + + /* no point in sleeping unless someone can free memory. */ + ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF, + cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + ih->ih_func = func; + ih->ih_arg = arg; + ih->ih_ipl = level; + ih->ih_irq = irqno; + ih->ih_name = name; + + TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list); + + if (name != NULL) + evcount_attach(&ih->ih_count, name, &ih->ih_irq); + +#ifdef DEBUG_INTC + printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level, + name); +#endif + intc_calc_mask(); + + restore_interrupts(psw); + return (ih); +} + +void +intc_intr_disestablish(void *cookie) +{ + int psw; + struct intrhand *ih = cookie; + int irqno = ih->ih_irq; + psw = disable_interrupts(PSR_I); + TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list); + if (ih->ih_name != NULL) + evcount_detach(&ih->ih_count); + free(ih, M_DEVBUF, 0); + restore_interrupts(psw); +} + +const char * +intc_intr_string(void *cookie) +{ + return "huh?"; +} + + +#if 0 +int intc_tst(void *a); + +int +intc_tst(void *a) +{ + printf("inct_tst called\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); + return 1; +} + +void intc_test(void); +void intc_test(void) +{ + void * ih; + printf("about to register handler\n"); + ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst"); + + printf("about to set bit\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2); + + printf("about to clear bit\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); + + printf("about to remove handler\n"); + intc_intr_disestablish(ih); + + printf("done\n"); +} +#endif diff --git a/intc.h b/intc.h new file mode 100644 index 0000000..bc0a59f --- /dev/null +++ b/intc.h @@ -0,0 +1,74 @@ +/* $OpenBSD: intc.h,v 1.2 2014/03/29 18:09:28 guenther Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OMAPINTC_INTR_H_ +#define _OMAPINTC_INTR_H_ + +#ifndef _LOCORE + +#include +#include +#include +#include + +extern volatile int current_spl_level; +extern volatile int softint_pending; +void intc_do_pending(void); + +#define SI_TO_IRQBIT(si) (1U<<(si)) +void intc_setipl(int new); +void intc_splx(int new); +int intc_splraise(int ipl); +int intc_spllower(int ipl); +void intc_setsoftintr(int si); + +/* + * An useful function for interrupt handlers. + * XXX: This shouldn't be here. + */ +static __inline int +find_first_bit( uint32_t bits ) +{ + int count; + + /* since CLZ is available only on ARMv5, this isn't portable + * to all ARM CPUs. This file is for OMAPINTC processor. + */ + asm( "clz %0, %1" : "=r" (count) : "r" (bits) ); + return 31-count; +} + + +/* + * This function *MUST* be called very early on in a port's + * initarm() function, before ANY spl*() functions are called. + * + * The parameter is the virtual address of the OMAPINTC's Interrupt + * Controller registers. + */ +void intc_intr_bootstrap(vaddr_t); + +void intc_irq_handler(void *); +void *intc_intr_establish(int irqno, int level, int (*func)(void *), + void *cookie, char *name); +void intc_intr_disestablish(void *cookie); +const char *intc_intr_string(void *cookie); + +#endif /* ! _LOCORE */ + +#endif /* _OMAPINTC_INTR_H_ */ + diff --git a/omap.c b/omap.c new file mode 100644 index 0000000..9cb946d --- /dev/null +++ b/omap.c @@ -0,0 +1,189 @@ +/* $OpenBSD: omap.c,v 1.9 2016/05/02 15:27:24 patrick Exp $ */ +/* + * Copyright (c) 2005,2008 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include +#include + +int omap_match(struct device *, void *, void *); +void omap3_init(); +void omap4_init(); +void am335x_init(); + +struct cfattach omap_ca = { + sizeof(struct armv7_softc), omap_match, armv7_attach +}; + +struct cfdriver omap_cd = { + NULL, "omap", DV_DULL +}; + +struct board_dev beagleboard_devs[] = { + { "prcm", 0 }, + { "intc", 0 }, + { "gptimer", 0 }, + { "gptimer", 1 }, + { "omdog", 0 }, + { "omgpio", 0 }, + { "omgpio", 1 }, + { "omgpio", 2 }, + { "omgpio", 3 }, + { "omgpio", 4 }, + { "omgpio", 5 }, + { "ommmc", 0 }, /* HSMMC1 */ + { "com", 2 }, /* UART3 */ + { NULL, 0 } +}; + +struct board_dev beaglebone_devs[] = { + { "prcm", 0 }, + { "sitaracm", 0 }, + { "intc", 0 }, + { "edma", 0 }, + { "dmtimer", 0 }, + { "dmtimer", 1 }, + { "omdog", 0 }, + { "omgpio", 0 }, + { "omgpio", 1 }, + { "omgpio", 2 }, + { "omgpio", 3 }, + { "tiiic", 0 }, + { "tiiic", 1 }, + { "tiiic", 2 }, + { "ommmc", 0 }, /* HSMMC0 */ + { "ommmc", 1 }, /* HSMMC1 */ + { "com", 0 }, /* UART0 */ + { "cpsw", 0 }, + { NULL, 0 } +}; + +struct board_dev overo_devs[] = { + { "prcm", 0 }, + { "intc", 0 }, + { "gptimer", 0 }, + { "gptimer", 1 }, + { "omdog", 0 }, + { "omgpio", 0 }, + { "omgpio", 1 }, + { "omgpio", 2 }, + { "omgpio", 3 }, + { "omgpio", 4 }, + { "omgpio", 5 }, + { "ommmc", 0 }, /* HSMMC1 */ + { "com", 2 }, /* UART3 */ + { NULL, 0 } +}; + +struct board_dev pandaboard_devs[] = { + { "omapid", 0 }, + { "prcm", 0 }, + { "omdog", 0 }, + { "omgpio", 0 }, + { "omgpio", 1 }, + { "omgpio", 2 }, + { "omgpio", 3 }, + { "omgpio", 4 }, + { "omgpio", 5 }, + { "ommmc", 0 }, /* HSMMC1 */ + { "com", 2 }, /* UART3 */ + { "ehci", 0 }, + { NULL, 0 } +}; + +struct armv7_board omap_boards[] = { + { + BOARD_ID_OMAP3_BEAGLE, + "TI OMAP3 BeagleBoard", + beagleboard_devs, + omap3_init, + }, + { + BOARD_ID_AM335X_BEAGLEBONE, + "TI AM335x BeagleBone", + beaglebone_devs, + am335x_init, + }, + { + BOARD_ID_OMAP3_OVERO, + "Gumstix OMAP3 Overo", + overo_devs, + omap3_init, + }, + { + BOARD_ID_OMAP4_PANDA, + "TI OMAP4 PandaBoard", + pandaboard_devs, + omap4_init, + }, + { 0, NULL, NULL, NULL }, +}; + +struct board_dev * +omap_board_devs(void) +{ + int i; + + for (i = 0; omap_boards[i].name != NULL; i++) { + if (omap_boards[i].board_id == board_id) + return (omap_boards[i].devs); + } + return (NULL); +} + +void +omap_board_init(void) +{ + int i; + + for (i = 0; omap_boards[i].name != NULL; i++) { + if (omap_boards[i].board_id == board_id) { + omap_boards[i].init(); + break; + } + } +} + +const char * +omap_board_name(void) +{ + int i; + + for (i = 0; omap_boards[i].name != NULL; i++) { + if (omap_boards[i].board_id == board_id) + return (omap_boards[i].name); + } + return (NULL); +} + +int +omap_match(struct device *parent, void *cfdata, void *aux) +{ + union mainbus_attach_args *ma = (union mainbus_attach_args *)aux; + struct cfdata *cf = (struct cfdata *)cfdata; + + if (ma->ma_name == NULL) + return (0); + + if (strcmp(cf->cf_driver->cd_name, ma->ma_name) != 0) + return (0); + + return (omap_board_devs() != NULL); +} diff --git a/omap3.c b/omap3.c new file mode 100644 index 0000000..bf2feb5 --- /dev/null +++ b/omap3.c @@ -0,0 +1,196 @@ +/* $OpenBSD: omap3.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */ + +/* + * Copyright (c) 2011 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include + +#define PRCM_ADDR 0x48004000 +#define PRCM_SIZE 0x2000 + +#define INTC_ADDR 0x48200000 +#define INTC_SIZE 0x200 + +#define GPTIMERx_SIZE 0x100 +#define GPTIMER1_ADDR 0x48318000 +#define GPTIMER1_IRQ 37 +#define GPTIMER2_ADDR 0x49032000 +#define GPTIMER2_IRQ 38 + +#define WD_ADDR 0x48314000 +#define WD_SIZE 0x80 + +#define GPIOx_SIZE 0x1000 +#define GPIO1_ADDR 0x48310000 +#define GPIO2_ADDR 0x49050000 +#define GPIO3_ADDR 0x49052000 +#define GPIO4_ADDR 0x49054000 +#define GPIO5_ADDR 0x49056000 +#define GPIO6_ADDR 0x49058000 + +#define GPIO1_IRQ 29 +#define GPIO2_IRQ 30 +#define GPIO3_IRQ 31 +#define GPIO4_IRQ 32 +#define GPIO5_IRQ 33 +#define GPIO6_IRQ 34 + +#define UARTx_SIZE 0x400 +#define UART1_ADDR 0x4806A000 +#define UART2_ADDR 0x4806C000 +#define UART3_ADDR 0x49020000 + +#define UART1_IRQ 72 +#define UART2_IRQ 73 +#define UART3_IRQ 74 + +#define HSMMCx_SIZE 0x200 +#define HSMMC1_ADDR 0x4809c000 +#define HSMMC1_IRQ 83 + +#define USBTLL_ADDR 0x48062000 +#define USBTLL_SIZE 0x1000 + +struct armv7_dev omap3_devs[] = { + + /* + * Power, Reset and Clock Manager + */ + + { .name = "prcm", + .unit = 0, + .mem = { { PRCM_ADDR, PRCM_SIZE } }, + }, + + /* + * Interrupt Controller + */ + + { .name = "intc", + .unit = 0, + .mem = { { INTC_ADDR, INTC_SIZE } }, + }, + + /* + * General Purpose Timers + */ + + { .name = "gptimer", + .unit = 1, /* XXX see gptimer.c */ + .mem = { { GPTIMER1_ADDR, GPTIMERx_SIZE } }, + .irq = { GPTIMER1_IRQ } + }, + + { .name = "gptimer", + .unit = 0, /* XXX see gptimer.c */ + .mem = { { GPTIMER2_ADDR, GPTIMERx_SIZE } }, + .irq = { GPTIMER2_IRQ } + }, + + /* + * GPIO + */ + + { .name = "omgpio", + .unit = 0, + .mem = { { GPIO1_ADDR, GPIOx_SIZE } }, + .irq = { GPIO1_IRQ } + }, + + { .name = "omgpio", + .unit = 1, + .mem = { { GPIO2_ADDR, GPIOx_SIZE } }, + .irq = { GPIO2_IRQ } + }, + + { .name = "omgpio", + .unit = 2, + .mem = { { GPIO3_ADDR, GPIOx_SIZE } }, + .irq = { GPIO3_IRQ } + }, + + { .name = "omgpio", + .unit = 3, + .mem = { { GPIO4_ADDR, GPIOx_SIZE } }, + .irq = { GPIO4_IRQ } + }, + + { .name = "omgpio", + .unit = 4, + .mem = { { GPIO5_ADDR, GPIOx_SIZE } }, + .irq = { GPIO5_IRQ } + }, + + { .name = "omgpio", + .unit = 5, + .mem = { { GPIO6_ADDR, GPIOx_SIZE } }, + .irq = { GPIO6_IRQ } + }, + + /* + * Watchdog Timer + */ + + { .name = "omdog", + .unit = 0, + .mem = { { WD_ADDR, WD_SIZE } } + }, + + /* + * UART + */ + + { .name = "com", + .unit = 2, + .mem = { { UART3_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + + /* + * MMC + */ + + { .name = "ommmc", + .unit = 0, + .mem = { { HSMMC1_ADDR, HSMMCx_SIZE } }, + .irq = { HSMMC1_IRQ } + }, + + /* + * USB + */ + + { .name = "omusbtll", + .unit = 0, + .mem = { { USBTLL_ADDR, USBTLL_SIZE } }, + }, + + /* Terminator */ + { .name = NULL, + .unit = 0, + } +}; + +void +omap3_init(void) +{ + armv7_set_devs(omap3_devs); +} diff --git a/omap3_prcmreg.h b/omap3_prcmreg.h new file mode 100644 index 0000000..02f5e7e --- /dev/null +++ b/omap3_prcmreg.h @@ -0,0 +1,222 @@ +/* $OpenBSD: omap3_prcmreg.h,v 1.3 2013/10/28 11:11:50 rapha Exp $ */ +/* + * Copyright (c) 2007, 2009, 2012 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* XXX - verify these definitions and correctly merge them with omap4 */ +#define CM_FCLKEN_IVA2 0x0000 +#define CM_CLKEN_PLL_IVA2 0x0004 +#define CM_IDLEST_IVA2 0x0020 +#define CM_IDLEST_PLL_IVA2 0x0024 +#define CM_AUTOIDLE_PLL_IVA2 0x0034 +#define CM_CLKSEL1_PLL_IVA2 0x0040 +#define CM_CLKSEL2_PLL_IVA2 0x0044 +#define CM_CLKSTCTRL_IVA2 0x0048 +#define CM_CLKSTST_IVA2 0x004c +#define CM_CLKSEL_MPU 0x0940 +#define CM_CLKSTCTRL_MPU 0x0948 +#define RM_RSTST_MPU 0x0958 +#define PM_WKDEP_MPU 0x09C8 +#define PM_EVGENCTRL_MPU 0x09D4 +#define PM_EVEGENONTIM_MPU 0x09D8 +#define PM_EVEGENOFFTIM_MPU 0x09DC +#define PM_PWSTCTRL_MPU 0x09E0 +#define PM_PWSTST_MPU 0x09E4 +#define CM_FCLKEN1_CORE 0x0a00 +#define CM_FCLKEN1_CORE_MSK 0x41fffe00 +#define CM_FCLKEN2_CORE 0x0a04 +#define CM_FCLKEN2_CORE_MSK 0x00000000 +#define CM_FCLKEN3_CORE 0x0a08 +#define CM_FCLKEN3_CORE_MSK 0x00000007 +#define CM_ICLKEN1_CORE 0x0a10 +#define CM_ICLKEN1_CORE_MSK 0x7ffffed2 +#define CM_ICLKEN2_CORE 0x0a14 +#define CM_ICLKEN2_CORE_MSK 0x0000001f +#define CM_ICLKEN3_CORE 0x0a18 +#define CM_ICLKEN3_CORE_MSK 0x00000004 +#define CM_ICLKEN4_CORE 0x0a1C +#define CM_IDLEST1_CORE 0x0a20 +#define CM_IDLEST2_CORE 0x0a24 +#define CM_IDLEST4_CORE 0x0a2C +#define CM_AUTOIDLE1_CORE 0x0a30 +#define CM_AUTOIDLE2_CORE 0x0a34 +#define CM_AUTOIDLE3_CORE 0x0a38 +#define CM_AUTOIDLE4_CORE 0x0a3C +#define CM_CLKSEL1_CORE 0x0a40 +#define CM_CLKSEL2_CORE 0x0a44 +#define CM_CLKSTCTRL_CORE 0x0a48 +#define PM_WKEN1_CORE 0x0aA0 +#define PM_WKEN2_CORE 0x0aA4 +#define PM_WKST1_CORE 0x0aB0 +#define PM_WKST2_CORE 0x0aB4 +#define PM_WKDEP_CORE 0x0aC8 +#define PM_PWSTCTRL_CORE 0x0aE0 +#define PM_PWSTST_CORE 0x0aE4 +#define CM_FCLKEN_GFX 0x0b00 +#define CM_ICLKEN_GFX 0x0b10 +#define CM_IDLEST_GFX 0x0b20 +#define CM_CLKSEL_GFX 0x0b40 +#define CM_CLKSTCTRL_GFX 0x0b48 +#define RM_RSTCTRL_GFX 0x0b50 +#define RM_RSTST_GFX 0x0b58 +#define PM_WKDEP_GFX 0x0bC8 +#define PM_PWSTCTRL_GFX 0x0bE0 +#define PM_PWSTST_GFX 0x0bE4 +#define CM_FCLKEN_WKUP 0x0c00 +#define CM_FCLKEN_WKUP_MSK 0x00000029 +#define CM_ICLKEN_WKUP 0x0c10 +#define CM_ICLKEN_WKUP_MSK 0x0000002d +#define CM_IDLEST_WKUP 0x0c20 +#define CM_AUTOIDLE_WKUP 0x0c30 +#define CM_CLKSEL_WKUP 0x0c40 +#define RM_RSTCTRL_WKUP 0x0c50 +#define RM_RSTTIME_WKUP 0x0c54 +#define RM_RSTST_WKUP 0x0c58 +#define PM_WKEN_WKUP 0x0cA0 +#define PM_WKST_WKUP 0x0cB0 +#define CM_CLKEN_PLL 0x0d00 +#define CM_CLKEN2_PLL 0x0d04 +#define CM_IDLEST_CKGEN 0x0d20 +#define CM_AUTOIDLE_PLL 0x0d30 +#define CM_AUTOIDLE2_PLL 0x0d34 +#define CM_CLKSEL1_PLL 0x0d40 +#define CM_CLKSEL2_PLL 0x0d44 +#define CM_CLKSEL3_PLL 0x0d48 +#define CM_CLKSEL4_PLL 0x0d4C +#define CM_CLKSEL5_PLL 0x0d50 +#define CM_FCLKEN_PER 0x1000 +#define CM_FCLKEN_PER_MSK 0x0003ffff +#define CM_ICLKEN_PER 0x1010 +#define CM_ICLKEN_PER_MSK 0x0003ffff +#define CM_IDLEST_PER 0x1020 +#define CM_AUTOIDLE_PER 0x1030 +#define CM_CLKSEL_PER 0x1040 +#define CM_SLEEPDEP_PER 0x1044 +#define CM_CLKSTCTRL_PER 0x1048 +#define CM_CLKSTST_PER 0x104C +#define CM_CLKSEL1_EMU 0x1140 +#define CM_CLKSTCTRL_EMU 0x1148 +#define CM_CLKSTST_EMU 0x114C +#define CM_CLKSEL2_EMU 0x1150 +#define CM_CLKSEL3_EMU 0x1154 +#define CM_POLCTRL 0x129C +#define CM_IDLEST_NEON 0x1320 +#define CM_CLKSTCTRL_NEON 0x1348 +#define CM_FCLKEN_USBHOST 0x1400 +#define CM_FCLKEN_USBHOST_MSK 0x00000003 +#define CM_ICLKEN_USBHOST 0x1410 +#define CM_ICLKEN_USBHOST_MSK 0x00000001 +#define CM_IDLEST_USBHOST 0x1420 +#define CM_AUTOIDLE_USBHOST 0x1430 +#define CM_SLEEPDEP_USBHOST 0x1444 +#define CM_CLKSTCTRL_USBHOST 0x1448 +#define CM_CLKSTST_USBHOST 0x144C + + +#define PRCM_REG_CORE_CLK1 0 +#define PRCM_REG_CORE_CLK1_FADDR CM_FCLKEN1_CORE +#define PRCM_REG_CORE_CLK1_IADDR CM_ICLKEN1_CORE +#define PRCM_REG_CORE_CLK1_FMASK CM_FCLKEN1_CORE_MSK +#define PRCM_REG_CORE_CLK1_IMASK CM_ICLKEN1_CORE_MSK +#define PRCM_REG_CORE_CLK1_BASE (PRCM_REG_CORE_CLK1*32) +#define PRCM_CLK_EN_MMC3 (PRCM_REG_CORE_CLK1_BASE + 30) +#define PRCM_CLK_EN_ICR (PRCM_REG_CORE_CLK1_BASE + 29) +#define PRCM_CLK_EN_AES2 (PRCM_REG_CORE_CLK1_BASE + 28) +#define PRCM_CLK_EN_SHA12 (PRCM_REG_CORE_CLK1_BASE + 27) +#define PRCM_CLK_EN_DES2 (PRCM_REG_CORE_CLK1_BASE + 26) +#define PRCM_CLK_EN_MMC2 (PRCM_REG_CORE_CLK1_BASE + 25) +#define PRCM_CLK_EN_MMC1 (PRCM_REG_CORE_CLK1_BASE + 24) +#define PRCM_CLK_EN_MSPRO (PRCM_REG_CORE_CLK1_BASE + 23) +#define PRCM_CLK_EN_HDQ (PRCM_REG_CORE_CLK1_BASE + 22) +#define PRCM_CLK_EN_MCSPI4 (PRCM_REG_CORE_CLK1_BASE + 21) +#define PRCM_CLK_EN_MCSPI3 (PRCM_REG_CORE_CLK1_BASE + 20) +#define PRCM_CLK_EN_MCSPI2 (PRCM_REG_CORE_CLK1_BASE + 19) +#define PRCM_CLK_EN_MCSPI1 (PRCM_REG_CORE_CLK1_BASE + 18) +#define PRCM_CLK_EN_I2C3 (PRCM_REG_CORE_CLK1_BASE + 17) +#define PRCM_CLK_EN_I2C2 (PRCM_REG_CORE_CLK1_BASE + 16) +#define PRCM_CLK_EN_I2C1 (PRCM_REG_CORE_CLK1_BASE + 15) +#define PRCM_CLK_EN_UART2 (PRCM_REG_CORE_CLK1_BASE + 14) +#define PRCM_CLK_EN_UART1 (PRCM_REG_CORE_CLK1_BASE + 13) +#define PRCM_CLK_EN_GPT11 (PRCM_REG_CORE_CLK1_BASE + 12) +#define PRCM_CLK_EN_GPT10 (PRCM_REG_CORE_CLK1_BASE + 11) +#define PRCM_CLK_EN_MCBSP5 (PRCM_REG_CORE_CLK1_BASE + 10) +#define PRCM_CLK_EN_MCBSP1 (PRCM_REG_CORE_CLK1_BASE + 9) +#define PRCM_CLK_EN_MAILBOXES (PRCM_REG_CORE_CLK1_BASE + 7) +#define PRCM_CLK_EN_OMAPCTRL (PRCM_REG_CORE_CLK1_BASE + 6) +#define PRCM_CLK_EN_HSOTGUSB (PRCM_REG_CORE_CLK1_BASE + 4) +#define PRCM_CLK_EN_SDRC (PRCM_REG_CORE_CLK1_BASE + 1) + +#define PRCM_REG_CORE_CLK2 1 +#define PRCM_REG_CORE_CLK2_FADDR CM_FCLKEN2_CORE +#define PRCM_REG_CORE_CLK2_IADDR CM_ICLKEN2_CORE +#define PRCM_REG_CORE_CLK2_FMASK CM_FCLKEN2_CORE_MSK +#define PRCM_REG_CORE_CLK2_IMASK CM_ICLKEN2_CORE_MSK +#define PRCM_REG_CORE_CLK2_BASE (PRCM_REG_CORE_CLK2*32) + +#define PRCM_REG_CORE_CLK3 2 +#define PRCM_REG_CORE_CLK3_FADDR CM_FCLKEN3_CORE +#define PRCM_REG_CORE_CLK3_IADDR CM_ICLKEN3_CORE +#define PRCM_REG_CORE_CLK3_FMASK CM_FCLKEN3_CORE_MSK +#define PRCM_REG_CORE_CLK3_IMASK CM_ICLKEN3_CORE_MSK +#define PRCM_REG_CORE_CLK3_BASE (PRCM_REG_CORE_CLK3*32) +#define PRCM_CORE_EN_USBTLL (PRCM_REG_CORE_CLK3_BASE + 2) +#define PRCM_CORE_EN_TS (PRCM_REG_CORE_CLK3_BASE + 1) +#define PRCM_CORE_EN_CPEFUSE (PRCM_REG_CORE_CLK3_BASE + 0) + +#define PRCM_REG_WKUP 3 +#define PRCM_REG_WKUP_FADDR CM_FCLKEN_WKUP +#define PRCM_REG_WKUP_IADDR CM_ICLKEN_WKUP +#define PRCM_REG_WKUP_FMASK CM_FCLKEN_WKUP_MSK +#define PRCM_REG_WKUP_IMASK CM_ICLKEN_WKUP_MSK +#define PRCM_REG_WKUP_BASE (PRCM_REG_WKUP*32) +#define PRCM_CLK_EN_MPU_WDT (PRCM_REG_WKUP_BASE + 5) +#define PRCM_CLK_EN_GPIO1 (PRCM_REG_WKUP_BASE + 3) +#define PRCM_CLK_EN_32KSYNC (PRCM_REG_WKUP_BASE + 2) +#define PRCM_CLK_EN_GPT1 (PRCM_REG_WKUP_BASE + 0) + +#define PRCM_REG_PER 4 +#define PRCM_REG_PER_FADDR CM_FCLKEN_PER +#define PRCM_REG_PER_IADDR CM_ICLKEN_PER +#define PRCM_REG_PER_FMASK CM_FCLKEN_PER_MSK +#define PRCM_REG_PER_IMASK CM_ICLKEN_PER_MSK +#define PRCM_REG_PER_BASE (PRCM_REG_PER*32) +#define PRCM_CLK_EN_GPIO6 (PRCM_REG_PER_BASE + 17) +#define PRCM_CLK_EN_GPIO5 (PRCM_REG_PER_BASE + 16) +#define PRCM_CLK_EN_GPIO4 (PRCM_REG_PER_BASE + 15) +#define PRCM_CLK_EN_GPIO3 (PRCM_REG_PER_BASE + 14) +#define PRCM_CLK_EN_GPIO2 (PRCM_REG_PER_BASE + 13) +#define PRCM_CLK_EN_WDT3 (PRCM_REG_PER_BASE + 12) +#define PRCM_CLK_EN_UART3 (PRCM_REG_PER_BASE + 11) +#define PRCM_CLK_EN_GPT9 (PRCM_REG_PER_BASE + 10) +#define PRCM_CLK_EN_GPT8 (PRCM_REG_PER_BASE + 9) +#define PRCM_CLK_EN_GPT7 (PRCM_REG_PER_BASE + 8) +#define PRCM_CLK_EN_GPT6 (PRCM_REG_PER_BASE + 7) +#define PRCM_CLK_EN_GPT5 (PRCM_REG_PER_BASE + 6) +#define PRCM_CLK_EN_GPT4 (PRCM_REG_PER_BASE + 5) +#define PRCM_CLK_EN_GPT3 (PRCM_REG_PER_BASE + 4) +#define PRCM_CLK_EN_GPT2 (PRCM_REG_PER_BASE + 3) +#define PRCM_CLK_EN_MCBSP4 (PRCM_REG_PER_BASE + 2) +#define PRCM_CLK_EN_MCBSP3 (PRCM_REG_PER_BASE + 1) +#define PRCM_CLK_EN_MCBSP2 (PRCM_REG_PER_BASE + 0) + +#define PRCM_REG_USBHOST 5 +#define PRCM_REG_USBHOST_FADDR CM_FCLKEN_USBHOST +#define PRCM_REG_USBHOST_IADDR CM_ICLKEN_USBHOST +#define PRCM_REG_USBHOST_FMASK CM_FCLKEN_USBHOST_MSK +#define PRCM_REG_USBHOST_IMASK CM_ICLKEN_USBHOST_MSK +#define PRCM_REG_USBHOST_BASE (PRCM_REG_USBHOST*32) +#define PRCM_CLK_EN_USBHOST2 (PRCM_REG_USBHOST_BASE + 1) +#define PRCM_CLK_EN_USBHOST1 (PRCM_REG_USBHOST_BASE + 0) +#define PRCM_CLK_EN_USB (PRCM_REG_USBHOST_BASE + 0) diff --git a/omap4.c b/omap4.c new file mode 100644 index 0000000..5cd1b81 --- /dev/null +++ b/omap4.c @@ -0,0 +1,200 @@ +/* $OpenBSD: omap4.c,v 1.3 2013/11/06 19:03:07 syl Exp $ */ + +/* + * Copyright (c) 2011 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include + +#include + +#define OMAPID_ADDR 0x4a002000 +#define OMAPID_SIZE 0x1000 + +#define WD_ADDR 0x4a314000 +#define WD_SIZE 0x80 + +#define GPIOx_SIZE 0x1000 +#define GPIO1_ADDR 0x4a310000 +#define GPIO2_ADDR 0x48055000 +#define GPIO3_ADDR 0x48057000 +#define GPIO4_ADDR 0x48059000 +#define GPIO5_ADDR 0x4805b000 +#define GPIO6_ADDR 0x4805d000 + +#define GPIO1_IRQ 29 +#define GPIO2_IRQ 30 +#define GPIO3_IRQ 31 +#define GPIO4_IRQ 32 +#define GPIO5_IRQ 33 +#define GPIO6_IRQ 34 + +#define UARTx_SIZE 0x400 +#define UART1_ADDR 0x4806A000 +#define UART2_ADDR 0x4806C000 +#define UART3_ADDR 0x48020000 +#define UART4_ADDR 0x4806E000 + +#define UART1_IRQ 72 +#define UART2_IRQ 73 +#define UART3_IRQ 74 +#define UART4_IRQ 70 + +#define HSMMCx_SIZE 0x200 +#define HSMMC1_ADDR 0x4809c100 +#define HSMMC1_IRQ 83 + +#define PRM_ADDR 0x4a306000 +#define PRM_SIZE 0x2000 +#define CM1_ADDR 0x4a004000 +#define CM1_SIZE 0x1000 +#define CM2_ADDR 0x4a008000 +#define CM2_SIZE 0x2000 +#define SCRM_ADDR 0x4a30a000 +#define SCRM_SIZE 0x1000 +#define PCNF1_ADDR 0x4a100000 +#define PCNF1_SIZE 0x1000 +#define PCNF2_ADDR 0x4a31e000 +#define PCNF2_SIZE 0x1000 + +#define HSUSBHOST_ADDR 0x4a064000 +#define HSUSBHOST_SIZE 0x800 +#define USBEHCI_ADDR 0x4a064c00 +#define USBEHCI_SIZE 0x400 +#define USBOHCI_ADDR 0x4a064800 +#define USBOHCI_SIZE 0x400 +#define USBEHCI_IRQ 77 + +struct armv7_dev omap4_devs[] = { + + /* + * Power, Reset and Clock Manager + */ + + { .name = "prcm", + .unit = 0, + .mem = { + { PRM_ADDR, PRM_SIZE }, + { CM1_ADDR, CM1_SIZE }, + { CM2_ADDR, CM2_SIZE }, + }, + }, + + /* + * OMAP identification registers/fuses + */ + + { .name = "omapid", + .unit = 0, + .mem = { { OMAPID_ADDR, OMAPID_SIZE } }, + }, + + /* + * GPIO + */ + + { .name = "omgpio", + .unit = 0, + .mem = { { GPIO1_ADDR, GPIOx_SIZE } }, + .irq = { GPIO1_IRQ } + }, + + { .name = "omgpio", + .unit = 1, + .mem = { { GPIO2_ADDR, GPIOx_SIZE } }, + .irq = { GPIO2_IRQ } + }, + + { .name = "omgpio", + .unit = 2, + .mem = { { GPIO3_ADDR, GPIOx_SIZE } }, + .irq = { GPIO3_IRQ } + }, + + { .name = "omgpio", + .unit = 3, + .mem = { { GPIO4_ADDR, GPIOx_SIZE } }, + .irq = { GPIO4_IRQ } + }, + + { .name = "omgpio", + .unit = 4, + .mem = { { GPIO5_ADDR, GPIOx_SIZE } }, + .irq = { GPIO5_IRQ } + }, + + { .name = "omgpio", + .unit = 5, + .mem = { { GPIO6_ADDR, GPIOx_SIZE } }, + .irq = { GPIO6_IRQ } + }, + + /* + * Watchdog Timer + */ + + { .name = "omdog", + .unit = 0, + .mem = { { WD_ADDR, WD_SIZE } } + }, + + /* + * UART + */ + + { .name = "com", + .unit = 2, + .mem = { { UART3_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + + /* + * MMC + */ + + { .name = "ommmc", + .unit = 0, + .mem = { { HSMMC1_ADDR, HSMMCx_SIZE } }, + .irq = { HSMMC1_IRQ } + }, + + /* + * USB + */ + + { .name = "ehci", + .unit = 0, + .mem = { + { USBEHCI_ADDR, USBEHCI_SIZE }, + { HSUSBHOST_ADDR, HSUSBHOST_SIZE }, + }, + .irq = { USBEHCI_IRQ } + }, + + /* Terminator */ + { .name = NULL, + .unit = 0, + } +}; + +void +omap4_init(void) +{ + armv7_set_devs(omap4_devs); +} diff --git a/omap4_prcmreg.h b/omap4_prcmreg.h new file mode 100644 index 0000000..9cff537 --- /dev/null +++ b/omap4_prcmreg.h @@ -0,0 +1,27 @@ +/* $OpenBSD: omap4_prcmreg.h,v 1.1 2013/09/04 14:38:30 patrick Exp $ */ +/* + * Copyright (c) 2007, 2009, 2012 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define O4_L3INIT_CM2_OFFSET 0x00001300 +#define O4_CLKCTRL_MODULEMODE_MASK 0x00000003 +#define O4_CLKCTRL_MODULEMODE_DISABLE 0x00000000 +#define O4_CLKCTRL_MODULEMODE_AUTO 0x00000001 +#define O4_CLKCTRL_MODULEMODE_ENABLE 0x00000001 + +#define O4_MAX_MODULE_ENABLE_WAIT 1000 + +#define O4_CLKCTRL_IDLEST_MASK 0x00030000UL +#define O4_CLKCTRL_IDLEST_ENABLED 0x00000000UL diff --git a/omap_com.c b/omap_com.c new file mode 100644 index 0000000..9a3a94c --- /dev/null +++ b/omap_com.c @@ -0,0 +1,112 @@ +/* $OpenBSD: omap_com.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */ +/* + * Copyright 2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Steve C. Woodford for Wasabi Systems, Inc. + * + * 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 for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 +#include +#include +#include + +#include +#include + +#include +#include + +/* pick up armv7_a4x_bs_tag */ +#include + +#include + +#define com_isr 8 +#define ISR_RECV (ISR_RXPL | ISR_XMODE | ISR_RCVEIR) + +void omapuart_attach(struct device *, struct device *, void *); +int omapuart_activate(struct device *, int); + +struct cfattach com_omap_ca = { + sizeof (struct com_softc), NULL, omapuart_attach, NULL, + omapuart_activate +}; + +void +omapuart_attach(struct device *parent, struct device *self, void *aux) +{ + struct com_softc *sc = (struct com_softc *)self; + struct armv7_attach_args *aa = aux; + + sc->sc_iot = &armv7_a4x_bs_tag; /* XXX: This sucks */ + sc->sc_iobase = aa->aa_dev->mem[0].addr; + sc->sc_frequency = 48000000; + sc->sc_uarttype = COM_UART_TI16750; + + if (bus_space_map(sc->sc_iot, sc->sc_iobase, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) { + printf("%s: bus_space_map failed\n", __func__); + return; + } + + com_attach_subr(sc); + + (void)arm_intr_establish(aa->aa_dev->irq[0], IPL_TTY, comintr, + sc, sc->sc_dev.dv_xname); +} + +int +omapuart_activate(struct device *self, int act) +{ + struct com_softc *sc = (struct com_softc *)self; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + + switch (act) { + case DVACT_SUSPEND: + break; + case DVACT_RESUME: + if (sc->enabled) { + sc->sc_initialize = 1; + comparam(tp, &tp->t_termios); + bus_space_write_1(iot, ioh, com_ier, sc->sc_ier); + + if (ISSET(sc->sc_hwflags, COM_HW_SIR)) { + bus_space_write_1(iot, ioh, com_isr, + ISR_RECV); + } + } + break; + } + return 0; +} diff --git a/omap_machdep.c b/omap_machdep.c new file mode 100644 index 0000000..4e29a3e --- /dev/null +++ b/omap_machdep.c @@ -0,0 +1,137 @@ +/* $OpenBSD: omap_machdep.c,v 1.6 2015/05/19 03:30:54 jsg Exp $ */ +/* + * Copyright (c) 2013 Sylvestre Gallon + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +extern void omap4_smc_call(uint32_t, uint32_t); +extern void omdog_reset(void); +extern char *omap_board_name(void); +extern struct board_dev *omap_board_devs(void); +extern void omap_board_init(void); +extern int comcnspeed; +extern int comcnmode; + +void +omap_platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh, + bus_size_t off, uint32_t op, uint32_t val) +{ + switch (op) { + case 0x100: /* PL310 DEBUG */ + case 0x102: /* PL310 CTL */ + break; + default: + panic("platform_smc_write: invalid operation %d", op); + } + + omap4_smc_call(op, val); +} + +void +omap_platform_init_cons(void) +{ + paddr_t paddr; + + switch (board_id) { + case BOARD_ID_OMAP3_BEAGLE: + case BOARD_ID_OMAP3_OVERO: + paddr = 0x49020000; + break; + case BOARD_ID_AM335X_BEAGLEBONE: + paddr = 0x44e09000; + break; + case BOARD_ID_OMAP4_PANDA: + paddr = 0x48020000; + break; + } + + comcnattach(&armv7_a4x_bs_tag, paddr, comcnspeed, 48000000, comcnmode); + comdefaultrate = comcnspeed; +} + +void +omap_platform_watchdog_reset(void) +{ + omdog_reset(); +} + +void +omap_platform_powerdown(void) +{ + +} + +const char * +omap_platform_board_name(void) +{ + return (omap_board_name()); +} + +void +omap_platform_disable_l2_if_needed(void) +{ + switch (board_id) { + case BOARD_ID_OMAP4_PANDA: + /* disable external L2 cache */ + omap4_smc_call(0x102, 0); + break; + } +} + +void +omap_platform_board_init(void) +{ + omap_board_init(); +} + +struct armv7_platform omap_platform = { + .boot_name = "OpenBSD/omap", + .board_name = omap_platform_board_name, + .board_init = omap_platform_board_init, + .smc_write = omap_platform_smc_write, + .init_cons = omap_platform_init_cons, + .watchdog_reset = omap_platform_watchdog_reset, + .powerdown = omap_platform_powerdown, + .disable_l2_if_needed = omap_platform_disable_l2_if_needed, +}; + +struct armv7_platform * +omap_platform_match(void) +{ + struct board_dev *devs; + + devs = omap_board_devs(); + if (devs == NULL) + return (NULL); + + omap_platform.devs = devs; + return (&omap_platform); +} diff --git a/omapid.c b/omapid.c new file mode 100644 index 0000000..406c9d6 --- /dev/null +++ b/omapid.c @@ -0,0 +1,101 @@ +/* $OpenBSD: omapid.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */ +/* + * Copyright (c) 2013 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define O4_ID_SIZE 0x1000 +#define O4_FUSE_ID0 0x200 +#define O4_ID_CODE 0x204 +#define O4_FUSE_ID1 0x208 +#define O4_FUSE_ID2 0x20C +#define O4_FUSE_ID3 0x210 +#define O4_FUSE_PRODID0 0x214 +#define O4_FUSE_PRODID1 0x218 + + +struct omapid_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct omapid_softc *omapid_sc; + + +void omapid_attach(struct device *parent, struct device *self, void *args); +void omapid_wpending(int flags); + +struct cfattach omapid_ca = { + sizeof (struct omapid_softc), NULL, omapid_attach +}; + +struct cfdriver omapid_cd = { + NULL, "omapid", DV_DULL +}; + +void amptimer_set_clockrate(int32_t new_frequency); /* XXX */ + +void +omapid_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct omapid_softc *sc = (struct omapid_softc *) self; + uint32_t rev; + uint32_t newclockrate = 0; + char *board; + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("omapid: bus_space_map failed!"); + + omapid_sc = sc; + + board = "unknown"; + switch (board_id) { + case BOARD_ID_OMAP4_PANDA: + rev = bus_space_read_4(sc->sc_iot, sc->sc_ioh, O4_ID_CODE); + switch ((rev >> 12) & 0xffff) { + case 0xB852: + case 0xB95C: + board = "omap4430"; + newclockrate = 400 * 1000 * 1000; + break; + case 0xB94E: + board = "omap4460"; + newclockrate = 350 * 1000 * 1000; + break; + } + break; + default: + break; + } + printf(": %s\n", board); + if (newclockrate != 0) + amptimer_set_clockrate(newclockrate); +} diff --git a/omdisplay.c b/omdisplay.c new file mode 100644 index 0000000..7fe18c0 --- /dev/null +++ b/omdisplay.c @@ -0,0 +1,1394 @@ +/* $OpenBSD: omdisplay.c,v 1.5 2014/07/12 18:44:41 tedu Exp $ */ +/* + * Copyright (c) 2007 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "splash16.h" + +#define OMDISPLAY_SIZE 0x1000 +/* registers */ +/* DSS */ +#define DSS_REVISIONNUMBER 0x00 +#define DSS_CONTROL 0x40 +#define DSS_PSA_LCD_REG_1 0x50 +#define DSS_PSA_LCD_REG_2 0x54 +#define DSS_PSA_VIDEO_REG 0x58 +#define DSS_STATUS 0x5C + +/* DCR */ +#define DISPC_REVISION 0x0000 +#define DISPC_SYSCONFIG 0x0010 +#define DISPC_SYSCONFIG_AUTOIDLE 0x00000001 +#define DISPC_SYSCONFIG_SOFTRESET 0x00000002 +#define DISPC_SYSCONFIG_SIDLEMODE_FORCE 0x00000000 +#define DISPC_SYSCONFIG_SIDLEMODE_NONE 0x00000008 +#define DISPC_SYSCONFIG_SIDLEMODE_SMART 0x00000010 +#define DISPC_SYSCONFIG_MIDLEMODE_FORCE 0x00000000 +#define DISPC_SYSCONFIG_MIDLEMODE_NONE 0x00001000 +#define DISPC_SYSCONFIG_MIDLEMODE_SMART 0x00002000 +#define DISPC_SYSSTATUS 0x0014 +#define DISPC_SYSTATUS_RESETDONE 0x00000001 +#define DISPC_IRQSTATUS 0x0018 +#define DISPC_IRQSTATUS_FRAMEDONE 0x00000001 +#define DISPC_IRQSTATUS_VSYNC 0x00000002 +#define DISPC_IRQSTATUS_EVSYNCEVEN 0x00000004 +#define DISPC_IRQSTATUS_EVSYNCODD 0x00000008 +#define DISPC_IRQSTATUS_ACBIASCOUNT 0x00000010 +#define DISPC_IRQSTATUS_PROGLINENUM 0x00000020 +#define DISPC_IRQSTATUS_GFXFIFOUNDER 0x00000040 +#define DISPC_IRQSTATUS_GFXENDWINDOW 0x00000080 +#define DISPC_IRQSTATUS_PALGAMMA 0x00000100 +#define DISPC_IRQSTATUS_OCPERROR 0x00000200 +#define DISPC_IRQSTATUS_VID1FIFOUNDER 0x00000400 +#define DISPC_IRQSTATUS_VID1ENDWIND 0x00000800 +#define DISPC_IRQSTATUS_VID2FIFOUNDER 0x00001000 +#define DISPC_IRQSTATUS_VID2ENDWIND 0x00002000 +#define DISPC_IRQSTATUS_SYNCLOST 0x00004000 +#define DISPC_IRQENABLE 0x001C +#define DISPC_IRQENABLE_FRAMEDONE 0x00000001 +#define DISPC_IRQENABLE_VSYNC 0x00000002 +#define DISPC_IRQENABLE_EVSYNCEVEN 0x00000004 +#define DISPC_IRQENABLE_EVSYNCODD 0x00000008 +#define DISPC_IRQENABLE_ACBIASCOUNT 0x00000010 +#define DISPC_IRQENABLE_PROGLINENUM 0x00000020 +#define DISPC_IRQENABLE_GFXFIFOUNDER 0x00000040 +#define DISPC_IRQENABLE_GFXENDWINDOW 0x00000080 +#define DISPC_IRQENABLE_PALGAMMA 0x00000100 +#define DISPC_IRQENABLE_OCPERROR 0x00000200 +#define DISPC_IRQENABLE_VID1FIFOUNDER 0x00000400 +#define DISPC_IRQENABLE_VID1ENDWIND 0x00000800 +#define DISPC_IRQENABLE_VID2FIFOUNDER 0x00001000 +#define DISPC_IRQENABLE_VID2ENDWIND 0x00002000 +#define DISPC_IRQENABLE_SYNCLOST 0x00004000 +#define DISPC_CONTROL 0x0040 +#define DISPC_CONTROL_LCDENABLE 0x00000001 +#define DISPC_CONTROL_DIGITALENABLE 0x00000002 +#define DISPC_CONTROL_MONOCOLOR 0x00000004 +#define DISPC_CONTROL_STNTFT 0x00000008 +#define DISPC_CONTROL_M8B 0x00000010 +#define DISPC_CONTROL_GOLCD 0x00000020 +#define DISPC_CONTROL_GODIGITAL 0x00000040 +#define DISPC_CONTROL_TFTDITHEREN 0x00000080 +#define DISPC_CONTROL_TFTDATALINES_12 0x00000000 +#define DISPC_CONTROL_TFTDATALINES_16 0x00000100 +#define DISPC_CONTROL_TFTDATALINES_18 0x00000200 +#define DISPC_CONTROL_TFTDATALINES_24 0x00000300 +#define DISPC_CONTROL_SECURE 0x00000400 +#define DISPC_CONTROL_RFBIMODE 0x00000800 +#define DISPC_CONTROL_OVERLAYOPT 0x00001000 +#define DISPC_CONTROL_GPIN0 0x00002000 +#define DISPC_CONTROL_GPIN1 0x00004000 +#define DISPC_CONTROL_GPOUT0 0x00008000 +#define DISPC_CONTROL_GPOUT1 0x00010000 +#define DISPC_CONTROL_HT 0x00070000 +#define DISPC_CONTROL_HT_s(x) ((x) << 17) +#define DISPC_CONTROL_TDMENABLE 0x00100000 +#define DISPC_CONTROL_TDMPARALLEL_8 0x00000000 +#define DISPC_CONTROL_TDMPARALLEL_9 0x00200000 +#define DISPC_CONTROL_TDMPARALLEL_12 0x00400000 +#define DISPC_CONTROL_TDMPARALLEL_16 0x00600000 +#define DISPC_CONTROL_TDMCYCLE_1 0x00000000 +#define DISPC_CONTROL_TDMCYCLE_2 0x00800000 +#define DISPC_CONTROL_TDMCYCLE_3 0x00000000 +#define DISPC_CONTROL_TDMCYCLE_3_2 0x01800000 +#define DISPC_CONTROL_TDMUNUSED_0 0x00000000 +#define DISPC_CONTROL_TDMUNUSED_1 0x02000000 +#define DISPC_CONTROL_TDMUNUSED_M 0x04000000 +#define DISPC_CONFIG 0x0044 +#define DISPC_CONFIG_PIXELGATED 0x00000001 +#define DISPC_CONFIG_LOADMODE_PGE 0x00000000 +#define DISPC_CONFIG_LOADMODE_PG 0x00000002 +#define DISPC_CONFIG_LOADMODE_DATA 0x00000004 +#define DISPC_CONFIG_LOADMODE_DATA_PG 0x00000006 +#define DISPC_CONFIG_PALETTEGAMMA 0x00000008 +#define DISPC_CONFIG_PIXELDATAGATED 0x00000010 +#define DISPC_CONFIG_PIXELCLOCKGATED 0x00000020 +#define DISPC_CONFIG_HSYNCGATED 0x00000040 +#define DISPC_CONFIG_VSYNCGATED 0x00000080 +#define DISPC_CONFIG_ACBIAGATED 0x00000100 +#define DISPC_CONFIG_FUNCGATED 0x00000200 +#define DISPC_CONFIG_TCKLCDEN 0x00000400 +#define DISPC_CONFIG_TCKLCDSEL 0x00000800 +#define DISPC_CONFIG_TCKDIGEN 0x00001000 +#define DISPC_CONFIG_TCKDIGSEL 0x00002000 +#define DISPC_CAPABLE 0x0048 +#define DISPC_DEFAULT_COLOR0 0x004C +#define DISPC_DEFAULT_COLOR1 0x0050 +#define DISPC_TRANS_COLOR0 0x0054 +#define DISPC_TRANS_COLOR1 0x0058 +#define DISPC_LINE_STATUS 0x005C +#define DISPC_LINE_NUMBER 0x0060 +#define DISPC_TIMING_H 0x0064 +#define DISPC_TIMING_H_HSW_s(x) ((x) << 0) +#define DISPC_TIMING_H_HFP_s(x) ((x) << 8) +#define DISPC_TIMING_H_HBP_s(x) ((x) << 20) +#define DISPC_TIMING_V 0x0068 +#define DISPC_TIMING_V_VSW_s(x) ((x) << 0) +#define DISPC_TIMING_V_VFP_s(x) ((x) << 8) +#define DISPC_TIMING_V_VBP_s(x) ((x) << 20) +#define DISPC_POL_FREQ 0x006C +#define DISPC_POL_FREQ_ACB_s(x) ((x) << 0) +#define DISPC_POL_FREQ_ACBI_s(x) ((x) << 8) +#define DISPC_POL_FREQ_IVS 0x00001000 +#define DISPC_POL_FREQ_IHS 0x00002000 +#define DISPC_POL_FREQ_IPC 0x00004000 +#define DISPC_POL_FREQ_IEO 0x00008000 +#define DISPC_POL_FREQ_RF 0x00010000 +#define DISPC_POL_FREQ_ONOFF 0x00020000 +#define DISPC_DIVISOR 0x0070 +#define DISPC_DIVISOR_PCD_s(x) ((x) << 0) +#define DISPC_DIVISOR_LCD_s(x) ((x) << 16) +#define DISPC_SIZE_DIG 0x0078 +#define DISPC_SIZE_DIG_PPL_s(x) ((x) << 0) +#define DISPC_SIZE_DIG_LPP_s(x) ((x) << 16) +#define DISPC_SIZE_LCD 0x007C +#define DISPC_SIZE_LCD_PPL_s(x) ((x) << 0) +#define DISPC_SIZE_LCD_LPP_s(x) ((x) << 16) +#define DISPC_GFX_BA0 0x0080 +#define DISPC_GFX_BA1 0x0084 +#define DISPC_GFX_POSITION 0x0088 +#define DISPC_GFX_SIZE 0x008C +#define DISPC_GFX_SIZE_X_s(x) ((x) << 0) +#define DISPC_GFX_SIZE_Y_s(x) ((x) << 16) +#define DISPC_GFX_ATTRIBUTES 0x00A0 +#define DISPC_GFX_ATTRIBUTES_GFXENABLE 0x001 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_1 0x000 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_2 0x002 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_4 0x004 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_8 0x006 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_12 0x008 +#define DISPC_GFX_ATTRIBUTES_GFXFMT_16 0x00c +#define DISPC_GFX_ATTRIBUTES_GFXFMT_24 0x010 +#define DISPC_GFX_ATTRIBUTES_GFXREPLICATE 0x020 +#define DISPC_GFX_ATTRIBUTES_BURST_4 0x000 +#define DISPC_GFX_ATTRIBUTES_BURST_8 0x040 +#define DISPC_GFX_ATTRIBUTES_BURST_16 0x080 +#define DISPC_GFX_ATTRIBUTES_GFXCHANNELOUT 0x100 +#define DISPC_GFX_ATTRIBUTES_NIBBLEMODE 0x200 +#define DISPC_GFX_ATTRIBUTES_ENDIAN 0x400 +#define DISPC_GFX_FIFO_THRESHOLD 0x00A4 +#define DISPC_GFX_FIFO_THRESHOLD_HIGH_SHIFT 16 +#define DISPC_GFX_FIFO_THRESHOLD_LOW_SHIFT 0 +#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8 +#define DISPC_GFX_ROW_INC 0x00AC +#define DISPC_GFX_PIXEL_INC 0x00B0 +#define DISPC_GFX_WINDOW_SKIP 0x00B4 +#define DISPC_GFX_TABLE_BA 0x00B8 +#define DISPC_VID1_BA0 0x00BC +#define DISPC_VID1_BA1 0x00C0 +#define DISPC_VID1_POSITION 0x00C4 +#define DISPC_VID1_SIZE 0x00C8 +#define DISPC_VID1_ATTRIBUTES 0x00CC +#define DISPC_VID1_FIFO_THRESHOLD 0x00D0 +#define DISPC_VID1_FIFO_SIZE_STATUS 0x00D4 +#define DISPC_VID1_ROW_INC 0x00D8 +#define DISPC_VID1_PIXEL_INC 0x00DC +#define DISPC_VID1_FIR 0x00E0 +#define DISPC_VID1_PICTURE_SIZE 0x00E4 +#define DISPC_VID1_ACCU0 0x00E8 +#define DISPC_VID1_ACCU1 0x00EC +#define DISPC_VID1_FIR_COEF_H0 0x00F0 +#define DISPC_VID1_FIR_COEF_H1 0x00F8 +#define DISPC_VID1_FIR_COEF_H2 0x0100 +#define DISPC_VID1_FIR_COEF_H3 0x0108 +#define DISPC_VID1_FIR_COEF_H4 0x0110 +#define DISPC_VID1_FIR_COEF_H5 0x0118 +#define DISPC_VID1_FIR_COEF_H6 0x0120 +#define DISPC_VID1_FIR_COEF_H7 0x0128 +#define DISPC_VID1_FIR_COEF_HV0 0x00F4 +#define DISPC_VID1_FIR_COEF_HV1 0x00FC +#define DISPC_VID1_FIR_COEF_HV2 0x0104 +#define DISPC_VID1_FIR_COEF_HV3 0x010C +#define DISPC_VID1_FIR_COEF_HV4 0x0114 +#define DISPC_VID1_FIR_COEF_HV5 0x011C +#define DISPC_VID1_FIR_COEF_HV6 0x0124 +#define DISPC_VID1_FIR_COEF_HV7 0x012C +#define DISPC_VID1_CONV_COEF0 0x0130 +#define DISPC_VID1_CONV_COEF1 0x0134 +#define DISPC_VID1_CONV_COEF2 0x0138 +#define DISPC_VID1_CONV_COEF3 0x013C +#define DISPC_VID1_CONV_COEF4 0x0140 +#define DISPC_VID2_BA0 0x014C +#define DISPC_VID2_BA1 0x0150 +#define DISPC_VID2_POSITION 0x0154 +#define DISPC_VID2_SIZE 0x0158 +#define DISPC_VID2_ATTRIBUTES 0x015C +#define DISPC_VID2_FIFO_THRESHOLD 0x0160 +#define DISPC_VID2_FIFO_SIZE_STATUS 0x0164 +#define DISPC_VID2_ROW_INC 0x0168 +#define DISPC_VID2_PIXEL_INC 0x016C +#define DISPC_VID2_FIR 0x0170 +#define DISPC_VID2_PICTURE_SIZE 0x0174 +#define DISPC_VID2_ACCU0 0x0178 +#define DISPC_VID2_ACCU1 0x017C +#define DISPC_VID2_FIR_COEF_H0 0x0180 +#define DISPC_VID2_FIR_COEF_H1 0x0188 +#define DISPC_VID2_FIR_COEF_H2 0x0190 +#define DISPC_VID2_FIR_COEF_H3 0x0198 +#define DISPC_VID2_FIR_COEF_H4 0x01A0 +#define DISPC_VID2_FIR_COEF_H5 0x01A8 +#define DISPC_VID2_FIR_COEF_H6 0x01B0 +#define DISPC_VID2_FIR_COEF_H7 0x01B8 +#define DISPC_VID2_FIR_COEF_HV0 0x0184 +#define DISPC_VID2_FIR_COEF_HV1 0x018C +#define DISPC_VID2_FIR_COEF_HV2 0x0194 +#define DISPC_VID2_FIR_COEF_HV3 0x019C +#define DISPC_VID2_FIR_COEF_HV4 0x01A4 +#define DISPC_VID2_FIR_COEF_HV5 0x01AC +#define DISPC_VID2_FIR_COEF_HV6 0x01B4 +#define DISPC_VID2_FIR_COEF_HV7 0x01BC +#define DISPC_VID2_CONV_COEF0 0x01C0 +#define DISPC_VID2_CONV_COEF1 0x01C4 +#define DISPC_VID2_CONV_COEF2 0x01C8 +#define DISPC_VID2_CONV_COEF3 0x01CC +#define DISPC_VID2_CONV_COEF4 0x01D0 +#define DISPC_DATA_CYCLE1 0x01D4 +#define DISPC_DATA_CYCLE2 0x01D8 +#define DISPC_DATA_CYCLE3 0x01DC +#define DISPC_SIZE 0x0200 + +/* RFBI */ +#define RFBI_REVISION 0x0000 +#define RFBI_SYSCONFIG 0x0010 +#define RFBI_SYSSTATUS 0x0014 +#define RFBI_CONTROL 0x0040 +#define RFBI_PIXEL_CNT 0x0044 +#define RFBI_LINE_NUMBER 0x0048 +#define RFBI_CMD 0x004C +#define RFBI_PARAM 0x0050 +#define RFBI_DATA 0x0054 +#define RFBI_READ 0x0058 +#define RFBI_STATUS 0x005C +#define RFBI_CONFIG0 0x0060 +#define RFBI_CONFIG1 0x0078 +#define RFBI_ONOFF_TIME0 0x0064 +#define RFBI_ONOFF_TIME1 0x007C +#define RFBI_CYCLE_TIME0 0x0068 +#define RFBI_CYCLE_TIME1 0x0080 +#define RFBI_DATA_CYCLE1_0 0x006C +#define RFBI_DATA_CYCLE1_1 0x0084 +#define RFBI_DATA_CYCLE2_0 0x0070 +#define RFBI_DATA_CYCLE2_1 0x0088 +#define RFBI_DATA_CYCLE3_0 0x0074 +#define RFBI_DATA_CYCLE3_1 0x008C +#define RFBI_VSYNC_WIDTH 0x0090 +#define RFBI_HSYNC_WIDTH 0x0094 + +/* VENC1 */ +#define REV_ID 0x0000 +#define STATUS 0x0004 +#define F_CONTROL 0x0008 +#define VIDOUT_CTRL 0x0010 +#define SYNC_CTRL 0x0014 +#define LLEN 0x001C +#define FLENS 0x0020 +#define HFLTR_CTRL 0x0024 +#define CC_CARR_WSS_CARR 0x0028 +#define C_PHASE 0x002C +#define GAIN_U 0x0030 +#define GAIN_V 0x0034 +#define GAIN_Y 0x0038 +#define BLACK_LEVEL 0x003C +#define BLANK_LEVEL 0x0040 +#define X_COLOR 0x0044 +#define M_CONTROL 0x0048 +#define BSTAMP_WSS_DATA 0x004C +#define S_CARR 0x0050 +#define LINE21 0x0054 +#define LN_SEL 0x0058 +#define L21_WC_CTL 0x005C +#define HTRIGGER_VTRIGGER 0x0060 +#define SAVID_EAVID 0x0064 +#define FLEN_FAL 0x0068 +#define LAL_PHASE_RESET 0x006C +#define HS_INT_START_STOP_X 0x0070 +#define HS_EXT_START_STOP_X 0x0074 +#define VS_INT_START_X 0x0078 +#define VS_INT_STOP_X_VS_INT_START_Y 0x007C +#define VS_INT_STOP_Y_VS_EXT_START_X 0x0080 +#define VS_EXT_STOP_X_VS_EXT_START_Y 0x0084 +#define VS_EXT_STOP_Y 0x0088 +#define AVID_START_STOP_X 0x0090 +#define AVID_START_STOP_Y 0x0094 +#define FID_INT_START_X_FID_INT_START_Y 0x00A0 +#define FID_INT_OFFSET_Y_FID_EXT_START_X 0x00A4 +#define FID_EXT_START_Y_FID_EXT_OFFSET_Y 0x00A8 +#define TVDETGP_INT_START_STOP_X 0x00B0 +#define TVDETGP_INT_START_STOP_Y 0x00B4 +#define GEN_CTRL 0x00B8 +#define DAC_TST_DAC_A 0x00C4 +#define DAC_B_DAC_C 0x00C8 + + +/* NO CONSOLE SUPPORT */ + + +/* assumes 565 panel. */ +struct omdisplay_panel_data { + int width; + int height; + int horiz_sync_width; + int horiz_front_porch; + int horiz_back_porch; + int vert_sync_width; + int vert_front_porch; + int vert_back_porch; + int panel_flags; + int sync; + int depth; +#define PANEL_SYNC_H_ACTIVE_HIGH 1 +#define PANEL_SYNC_V_ACTIVE_HIGH 2 + int linebytes; +}; + +#define PIXELDEPTH 16 +#define PIXELWIDTH 2 + +struct omdisplay_panel_data default_panel = { + 240, /* Width */ + 322, /* Height */ + 9, 9, 19, /* horiz sync, fp, bp */ + 1, 2, 2, /* vert sync, fp, bp */ + 0, /* flags */ + 0, /* sync */ + PIXELDEPTH, + 240*PIXELWIDTH +}; + +struct omdisplay_screen { + LIST_ENTRY(omdisplay_screen) link; + + /* Frame buffer */ + bus_dmamap_t dma; + bus_dma_segment_t segs[1]; + int nsegs; + size_t buf_size; + size_t map_size; + void *buf_va; + int depth; + + /* rasterop */ + struct rasops_info rinfo; +}; + +struct omdisplay_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_dsioh; + bus_space_handle_t sc_dcioh; + bus_space_handle_t sc_rfbioh; + bus_space_handle_t sc_venioh; + bus_dma_tag_t sc_dma_tag; + + void *sc_ih; + + int sc_nscreens; + LIST_HEAD(,omdisplay_screen) sc_screens; + + struct omdisplay_panel_data *sc_geometry; + struct omdisplay_screen *sc_active; +}; + +int omdisplay_match(struct device *parent, void *v, void *aux); +void omdisplay_attach(struct device *parent, struct device *self, void *args); +int omdisplay_activate(struct device *, int); +int omdisplay_ioctl(void *v, u_long cmd, caddr_t data, int flag, + struct proc *p); +void omdisplay_burner(void *v, u_int on, u_int flags); +int omdisplay_show_screen(void *v, void *cookie, int waitok, + void (*cb)(void *, int, int), void *cbarg); +int omdisplay_param(struct omdisplay_softc *sc, ulong cmd, + struct wsdisplay_param *dp); +int omdisplay_max_brightness(void); +int omdisplay_get_brightness(void); +void omdisplay_set_brightness(int newval); +void omdisplay_set_brightness_internal(int newval); +int omdisplay_get_backlight(void); +void omdisplay_set_backlight(int on); +void omdisplay_blank(int blank); +void omdisplay_suspend(struct omdisplay_softc *sc); +void omdisplay_resume(struct omdisplay_softc *sc); +void omdisplay_initialize(struct omdisplay_softc *sc, + struct omdisplay_panel_data *geom); +void omdisplay_setup_rasops(struct omdisplay_softc *sc, + struct rasops_info *rinfo); +int omdisplay_alloc_screen(void *v, const struct wsscreen_descr *_type, + void **cookiep, int *curxp, int *curyp, long *attrp); +int omdisplay_new_screen(struct omdisplay_softc *sc, + struct omdisplay_screen *scr, int depth); +paddr_t omdisplay_mmap(void *v, off_t offset, int prot); +int omdisplay_load_font(void *, void *, struct wsdisplay_font *); +int omdisplay_list_font(void *, struct wsdisplay_font *); +void omdisplay_free_screen(void *v, void *cookie); +void omdisplay_start(struct omdisplay_softc *sc); +void omdisplay_stop(struct omdisplay_softc *sc); +int omdisplay_intr(void *v); +void omdisplay_dumpreg(struct omdisplay_softc *sc); + +struct cfattach omdisplay_ca = { + sizeof (struct omdisplay_softc), omdisplay_match, omdisplay_attach, + NULL, omdisplay_activate +}; + +struct cfdriver omdisplay_cd = { + NULL, "omdisplay", DV_DULL +}; + +struct wsdisplay_accessops omdisplay_accessops = { + .ioctl = omdisplay_ioctl, + .mmap = omdisplay_mmap, + .alloc_screen = omdisplay_alloc_screen, + .free_screen = omdisplay_free_screen, + .show_screen = omdisplay_show_screen, + .load_font = omdisplay_load_font, + .list_font = omdisplay_list_font, + .burn_screen = omdisplay_burner + +}; + +struct omdisplay_wsscreen_descr { + struct wsscreen_descr c; /* standard descriptor */ + int depth; /* bits per pixel */ + int flags; /* rasops flags */ +}; + +struct omdisplay_wsscreen_descr omdisplay_screen = { + { + "std" + }, + 16, /* bits per pixel */ + 0 /* rotate */ +}; + +const struct wsscreen_descr *omdisplay_scr_descr[] = { + &omdisplay_screen.c +}; + +/* XXX - what about flip phones with CLI */ +const struct wsscreen_list omdisplay_screen_list = { + sizeof omdisplay_scr_descr / sizeof omdisplay_scr_descr[0], + omdisplay_scr_descr +}; + + +int +omdisplay_match(struct device *parent, void *v, void *aux) +{ + /* XXX */ + return (1); +} + +void +omdisplay_attach(struct device *parent, struct device *self, void *args) +{ + struct ahb_attach_args *aa = args; + struct omdisplay_softc *sc = (struct omdisplay_softc *) self; + struct wsemuldisplaydev_attach_args wsaa; + + + sc->sc_iot = aa->aa_iot; + + if (bus_space_map(sc->sc_iot, aa->aa_addr, OMDISPLAY_SIZE, 0, + &sc->sc_dsioh)) + panic("omdisplay_attach: bus_space_map failed!"); + + if (bus_space_subregion(sc->sc_iot, sc->sc_dsioh, 0x400, 1024, + &sc->sc_dcioh)) + panic("omdisplay_attach: bus_space_submap failed!"); + + if (bus_space_subregion(sc->sc_iot, sc->sc_dsioh, 0x800, 1024, + &sc->sc_rfbioh)) + panic("omdisplay_attach: bus_space_submap failed!"); + + if (bus_space_subregion(sc->sc_iot, sc->sc_dsioh, 0xc00, 1024, + &sc->sc_venioh)) + panic("omdisplay_attach: bus_space_submap failed!"); + + + sc->sc_nscreens = 0; + LIST_INIT(&sc->sc_screens); + + sc->sc_dma_tag = aa->aa_dmat; + + sc->sc_ih = arm_intr_establish(aa->aa_intr, IPL_BIO /* XXX */, + omdisplay_intr, sc, sc->sc_dev.dv_xname); + + printf ("\n"); + + sc->sc_geometry = &default_panel; + + { + /* XXX - dummy? */ + struct rasops_info dummy; + + omdisplay_initialize(sc, sc->sc_geometry); + + /* + * Initialize a dummy rasops_info to compute fontsize and + * the screen size in chars. + */ + bzero(&dummy, sizeof(dummy)); + omdisplay_setup_rasops(sc, &dummy); + } + + wsaa.console = 0; + wsaa.scrdata = &omdisplay_screen_list; + wsaa.accessops = &omdisplay_accessops; + wsaa.accesscookie = sc; + wsaa.defaultscreens = 0; + + (void)config_found(self, &wsaa, wsemuldisplaydevprint); + + /* backlight? */ +} + + +int +omdisplay_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct omdisplay_softc *sc = v; + struct wsdisplay_fbinfo *wsdisp_info; + struct omdisplay_screen *scr = sc->sc_active; + int res = EINVAL; + + switch (cmd) { + case WSDISPLAYIO_GETPARAM: + case WSDISPLAYIO_SETPARAM: + res = omdisplay_param(sc, cmd, (struct wsdisplay_param *)data); + break; + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_PXALCD; /* XXX */ + break; + + case WSDISPLAYIO_GINFO: + wsdisp_info = (struct wsdisplay_fbinfo *)data; + + wsdisp_info->height = sc->sc_geometry->height; + wsdisp_info->width = sc->sc_geometry->width; + wsdisp_info->depth = 16; /* XXX */ + wsdisp_info->cmsize = 0; + break; + + case WSDISPLAYIO_GETCMAP: + case WSDISPLAYIO_PUTCMAP: + return EINVAL; /* XXX Colormap */ + + case WSDISPLAYIO_SVIDEO: + case WSDISPLAYIO_GVIDEO: + break; + + case WSDISPLAYIO_GCURPOS: + case WSDISPLAYIO_SCURPOS: + case WSDISPLAYIO_GCURMAX: + case WSDISPLAYIO_GCURSOR: + case WSDISPLAYIO_SCURSOR: + default: + return -1; /* not implemented */ + + case WSDISPLAYIO_LINEBYTES: + if (scr != NULL) + *(u_int *)data = scr->rinfo.ri_stride; + else + *(u_int *)data = 0; + break; + + } + + if (res == EINVAL) + res = omdisplay_ioctl(v, cmd, data, flag, p); + + return res; +} + +void +omdisplay_burner(void *v, u_int on, u_int flags) +{ + + omdisplay_set_brightness(on ? omdisplay_get_brightness() : 0); + + /* GPIO controls for appsliver */ + if (on) { + omgpio_set_bit(93); /* 1 enable backlight */ + omgpio_set_dir(93, OMGPIO_DIR_OUT); + omgpio_clear_bit(26); /* 0 enable LCD */ + omgpio_set_dir(26, OMGPIO_DIR_OUT); + } else { + omgpio_clear_bit(93); /* 0 disable backlt */ + omgpio_set_dir(93, OMGPIO_DIR_OUT); + omgpio_set_bit(26); /* 1 disable LCD */ + omgpio_set_dir(26, OMGPIO_DIR_OUT); + } +} + +int +omdisplay_show_screen(void *v, void *cookie, int waitok, + void (*cb)(void *, int, int), void *cbarg) +{ + struct omdisplay_softc *sc = v; + struct rasops_info *ri = cookie; + struct omdisplay_screen *scr = ri->ri_hw, *old; + + old = sc->sc_active; + if (old == scr) + return 0; + + if (old != NULL) + ; /* Stop old screen */ + + sc->sc_active = scr; + omdisplay_initialize(sc, sc->sc_geometry); + + /* Turn on LCD */ + omdisplay_burner(v, 1, 0); + + return (0); +} + + + +/* + * wsdisplay I/O controls + */ +int +omdisplay_param(struct omdisplay_softc *sc, ulong cmd, + struct wsdisplay_param *dp) +{ + int res = EINVAL; + + switch (dp->param) { + case WSDISPLAYIO_PARAM_BACKLIGHT: + if (cmd == WSDISPLAYIO_GETPARAM) { + dp->min = 0; + dp->max = 1; + dp->curval = omdisplay_get_backlight(); + res = 0; + } else if (cmd == WSDISPLAYIO_SETPARAM) { +/* XXX */ +// omdisplay_set_backlight(dp->curval); + res = 0; + } + break; + + case WSDISPLAYIO_PARAM_CONTRAST: + /* unsupported */ + res = ENOTTY; + break; + + case WSDISPLAYIO_PARAM_BRIGHTNESS: + if (cmd == WSDISPLAYIO_GETPARAM) { + dp->min = 1; + dp->max = omdisplay_max_brightness(); + dp->curval = omdisplay_get_brightness(); + res = 0; + } else if (cmd == WSDISPLAYIO_SETPARAM) { +/* XXX */ +// omdisplay_set_brightness(dp->curval); + res = 0; + } + break; + } + + return res; +} + + +/* + * LCD backlight + */ + +static int lcdbrightnesscurval = 1; +static int lcdislit = 1; +static int lcdisblank = 0; + +struct lcd_backlight { + int duty; /* LZ9JG18 DAC value */ + int cont; /* BACKLIGHT_CONT signal */ + int on; /* BACKLIGHT_ON signal */ +}; + +const struct lcd_backlight lcd_bl[] = { + { 0x00, 0, 0 }, /* 0: Off */ + { 0x00, 0, 1 }, /* 1: 0% */ + { 0x01, 0, 1 }, /* 2: 20% */ + { 0x07, 0, 1 }, /* 3: 40% */ + { 0x01, 1, 1 }, /* 4: 60% */ + { 0x07, 1, 1 }, /* 5: 80% */ + { 0x11, 1, 1 }, /* 6: 100% */ + { -1, -1, -1 } /* 7: Invalid */ +}; +#define CURRENT_BACKLIGHT lcd_bl + +int +omdisplay_max_brightness(void) +{ + int i; + + for (i = 0; CURRENT_BACKLIGHT[i].duty != -1; i++) + ; + return i - 1; +} + +int +omdisplay_get_brightness(void) +{ + + return lcdbrightnesscurval; +} + +void +omdisplay_set_brightness(int newval) +{ + int max; + + max = omdisplay_max_brightness(); + if (newval < 0) + newval = 0; + else if (newval > max) + newval = max; + + if (omdisplay_get_backlight() && !lcdisblank) + omdisplay_set_brightness_internal(newval); + + if (newval > 0) + lcdbrightnesscurval = newval; +} + +void +omdisplay_set_brightness_internal(int newval) +{ + static int curval = 1; + int i; + + /* + * It appears that the C3000 backlight can draw too much power if we + * switch it from a low to a high brightness. Increasing brightness + * in steps avoids this issue. + */ + if (newval > curval) { + for (i = curval + 1; i <= newval; i++) { +/* atlas controls */ + /* CURRENT_BACKLIGHT[newval].duty); */ + } + } else { +/* atlas controls */ + /* CURRENT_BACKLIGHT[newval].duty); */ + } + + curval = newval; +} + +int +omdisplay_get_backlight(void) +{ + + return lcdislit; +} + +void +omdisplay_set_backlight(int on) +{ + + if (!on) { + omdisplay_set_brightness(0); + lcdislit = 0; + } else { + lcdislit = 1; + omdisplay_set_brightness(omdisplay_get_brightness()); + } +} + +void +omdisplay_blank(int blank) +{ + + if (blank) { + omdisplay_set_brightness(0); + lcdisblank = 1; + } else { + lcdisblank = 0; + omdisplay_set_brightness(omdisplay_get_brightness()); + } +} + +void +omdisplay_suspend(struct omdisplay_softc *sc) +{ + if (sc->sc_active != NULL) { + omdisplay_stop(sc); + /* XXX disable clocks */ + } +} + +void +omdisplay_resume(struct omdisplay_softc *sc) +{ + if (sc->sc_active != NULL) { + /* XXX - clocks? */ + omdisplay_initialize(sc, sc->sc_geometry); + omdisplay_start(sc); + } +} + +void +omdisplay_activate(struct device *self, int act) +{ + struct omdisplay_softc *sc = (struct omdisplay_softc *)self; + + switch (act) { + case DVACT_SUSPEND: + omdisplay_set_brightness(0); + omdisplay_suspend(sc); + break; + case DVACT_RESUME: + omdisplay_resume(sc); + omdisplay_set_brightness(omdisplay_get_brightness()); + break; + } + return 0; +} + +void +omdisplay_initialize(struct omdisplay_softc *sc, + struct omdisplay_panel_data *geom) +{ + struct omdisplay_screen *scr; + u_int32_t reg; + u_int32_t mode; +#if 0 + int den, nom; /* pixel rate */ +#endif + + + reg = bus_space_read_4(sc->sc_iot, sc->sc_dcioh, DISPC_CONTROL); + + scr = sc->sc_active; + + if (reg & (DISPC_CONTROL_LCDENABLE|DISPC_CONTROL_DIGITALENABLE)) { + omdisplay_stop(sc); + } + + /* XXX - enable clocks */ + + /* disable all interrupts */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_IRQENABLE, 0); + + /* GPIOs ? */ + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_CONFIG, + DISPC_CONFIG_LOADMODE_PG|DISPC_CONFIG_LOADMODE_DATA); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DEFAULT_COLOR0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DEFAULT_COLOR1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_TRANS_COLOR0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_TRANS_COLOR1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_LINE_NUMBER, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DATA_CYCLE1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DATA_CYCLE2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DATA_CYCLE3, 0); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_SYSCONFIG, + DISPC_SYSCONFIG_SIDLEMODE_NONE| + DISPC_SYSCONFIG_MIDLEMODE_NONE); + +#if 0 + if (geom->panel_flags & LCDPANEL_TDM) { + nom = tdmflags >>8 & 0x3; + den = tdmflags & 0x3; + } else { + nom = 1; + den = 1; + } + hsync = geom->width*den/nom + geom->horiz_sync_width + + geom->horiz_front_porch + geom->horiz_back_porch; +#endif + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_TIMING_H, + DISPC_TIMING_H_HSW_s(geom->horiz_sync_width) | + DISPC_TIMING_H_HFP_s(geom->horiz_front_porch) | + DISPC_TIMING_H_HBP_s(geom->horiz_back_porch)); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_TIMING_V, + DISPC_TIMING_V_VSW_s(geom->vert_sync_width) | + DISPC_TIMING_V_VFP_s(geom->vert_front_porch) | + DISPC_TIMING_V_VBP_s(geom->vert_back_porch)); + + reg = 0; + if (geom->sync & PANEL_SYNC_H_ACTIVE_HIGH) + reg |= DISPC_POL_FREQ_IHS; + if (geom->sync & PANEL_SYNC_V_ACTIVE_HIGH) + reg |= DISPC_POL_FREQ_IVS; + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_POL_FREQ, reg); + + + /* clkdiv = pixclock/period; */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_SIZE_LCD, + DISPC_SIZE_LCD_PPL_s(geom->width-1) | + DISPC_SIZE_LCD_LPP_s(geom->height-1)); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_SIZE_DIG, + DISPC_SIZE_LCD_PPL_s(geom->width-1) | + DISPC_SIZE_LCD_LPP_s(geom->height-1)); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_SIZE, + DISPC_GFX_SIZE_X_s(geom->width-1) | + DISPC_GFX_SIZE_Y_s(geom->height-1)); + + + /* XXX!!! */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_DIVISOR, + DISPC_DIVISOR_LCD_s(1) | DISPC_DIVISOR_PCD_s(6)); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_BA0, + scr->segs[0].ds_addr); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_BA1, + scr->segs[0].ds_addr); + + /* non-rotated */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_PIXEL_INC, 1); + + + /* XXX 24bit -> 32 pixels */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_ROW_INC, + 1 + scr->rinfo.ri_stride - + (scr->rinfo.ri_width * scr->rinfo.ri_depth / 8)); + + switch (geom->depth) { + case 1: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_1; + break; + case 2: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_2; + break; + case 4: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_4; + break; + case 8: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_8; + break; + case 12: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_12; + break; + case 16: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_16; + break; + case 24: + mode = DISPC_GFX_ATTRIBUTES_GFXFMT_24; + break; + default: + panic("invalid depth %d", geom->depth); + } + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_ATTRIBUTES, + DISPC_GFX_ATTRIBUTES_GFXENABLE | mode | + DISPC_GFX_ATTRIBUTES_BURST_8); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_POSITION, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_WINDOW_SKIP, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_FIFO_THRESHOLD, + (0xfc << DISPC_GFX_FIFO_THRESHOLD_HIGH_SHIFT) | + (0xc0 << DISPC_GFX_FIFO_THRESHOLD_LOW_SHIFT)); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_ROW_INC, 1); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_PIXEL_INC, 1); + + /* DISPC_CONFIG_PALETTEGAMMA not enabled */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_GFX_TABLE_BA, + scr->segs[0].ds_addr); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_BA0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_BA1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_SIZE, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_ATTRIBUTES, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIFO_THRESHOLD, + 0xc00040); /* XXX */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIFO_SIZE_STATUS, + 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_ROW_INC, 1); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_PIXEL_INC, 1); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_PICTURE_SIZE, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_ACCU0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_ACCU1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H4, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H5, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H6, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_H7, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV4, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV5, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV6, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_FIR_COEF_HV7, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_CONV_COEF0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_CONV_COEF1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_CONV_COEF2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_CONV_COEF3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID1_CONV_COEF4, 0); + + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_BA0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_BA1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_SIZE, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_ATTRIBUTES, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIFO_THRESHOLD, + 0xc00040); /* XXX */ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIFO_SIZE_STATUS, + 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_ROW_INC, 1); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_PIXEL_INC, 1); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_PICTURE_SIZE, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_ACCU0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_ACCU1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H4, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H5, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H6, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_H7, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV4, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV5, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV6, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_FIR_COEF_HV7, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_CONV_COEF0, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_CONV_COEF1, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_CONV_COEF2, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_CONV_COEF3, 0); + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_VID2_CONV_COEF4, 0); + + omdisplay_start(sc); +} + +void +omdisplay_setup_rasops(struct omdisplay_softc *sc, struct rasops_info *rinfo) +{ + struct omdisplay_wsscreen_descr *descr; + struct omdisplay_panel_data *geom; + + descr = &omdisplay_screen; + geom = sc->sc_geometry; + + rinfo->ri_flg = descr->flags; + rinfo->ri_depth = descr->depth; + rinfo->ri_width = geom->width; + rinfo->ri_height = geom->height; + rinfo->ri_stride = geom->linebytes; + + /* pixel position */ + if (descr->depth == 16) { + rinfo->ri_rnum = 5; + rinfo->ri_rpos = 11; + rinfo->ri_gnum = 6; + rinfo->ri_gpos = 5; + rinfo->ri_bnum = 5; + rinfo->ri_bpos = 0; + } + + if (descr->c.nrows == 0) { + /* get rasops to compute screen size the first time */ + rasops_init(rinfo, 100, 100); + } else { + if (descr->flags != 0) /* rotate */ + rasops_init(rinfo, descr->c.ncols, descr->c.nrows); + else + rasops_init(rinfo, descr->c.nrows, descr->c.ncols); + } + + descr->c.nrows = rinfo->ri_rows; + descr->c.ncols = rinfo->ri_cols; + descr->c.capabilities = rinfo->ri_caps; + descr->c.textops = &rinfo->ri_ops; + +} + + +int +omdisplay_alloc_screen(void *v, const struct wsscreen_descr *_type, + void **cookiep, int *curxp, int *curyp, long *attrp) +{ + struct omdisplay_softc *sc = v; + struct omdisplay_screen *scr; + struct rasops_info *ri; + struct omdisplay_wsscreen_descr *type = + (struct omdisplay_wsscreen_descr *)_type; + int error; + + scr = malloc(sizeof *scr, M_DEVBUF, (cold ? M_NOWAIT : M_WAITOK)); + if (scr == NULL) + return (ENOMEM); + + error = omdisplay_new_screen(sc, scr, type->depth); + if (error != 0) { + free(scr, M_DEVBUF, 0); + return (error); + } + + /* + * initialize raster operation for this screen. + */ + ri = &scr->rinfo; + ri->ri_hw = (void *)scr; + ri->ri_bits = scr->buf_va; + omdisplay_setup_rasops(sc, ri); + + /* assumes 16 bpp */ + ri->ri_ops.alloc_attr(ri, 0, 0, 0, attrp); + + *cookiep = ri; + *curxp = 0; + *curyp = 0; + + return 0; +} + +/* + * Create and initialize a new screen buffer. + */ +int +omdisplay_new_screen(struct omdisplay_softc *sc, + struct omdisplay_screen *scr, int depth) +{ + bus_space_tag_t iot; + bus_space_handle_t ioh; + bus_dma_tag_t dma_tag; + struct omdisplay_panel_data *geometry; + int width, height; + bus_size_t size; + int error, palette_size; + int busdma_flag = (cold ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + + if (sc != NULL) { + iot = sc->sc_iot; + ioh = sc->sc_dcioh; + dma_tag = sc->sc_dma_tag; + geometry = sc->sc_geometry; + } else { + /* We are creating the console screen. */ +#if 0 + iot = omdisplay_console.iot; + ioh = omdisplay_console.ioh; + dma_tag = omdisplay_console.dma_tag; + geometry = omdisplay_console.geometry; +#endif + } + + width = geometry->width; + height = geometry->height; + palette_size = 0; + + switch (depth) { + case 1: + case 2: + case 4: + case 8: + palette_size = (1 << depth) * sizeof (uint16_t); + /* FALLTHROUGH */ + case 16: + case 24: + size = geometry->height * geometry->linebytes; + break; + default: + printf("%s: Unknown depth (%d)\n", + sc != NULL ? sc->sc_dev.dv_xname : "console", depth); + return (EINVAL); + } + + bzero(scr, sizeof *scr); + + scr->nsegs = 0; + scr->depth = depth; + scr->buf_size = size; + scr->buf_va = NULL; + size = roundup(size, 16); +#if 0 + + 3 * sizeof (struct lcd_dma_descriptor) + + palette_size; +#endif + + error = bus_dmamem_alloc(dma_tag, size, 0x100000, 0, + scr->segs, 1, &(scr->nsegs), busdma_flag); + if (error != 0 || scr->nsegs != 1) { + /* XXX: Actually we can handle nsegs > 1 case by means + of multiple DMA descriptors for a panel. It would + make code here a bit hairy */ + if (error == 0) + error = E2BIG; + goto bad; + } + + error = bus_dmamem_map(dma_tag, scr->segs, scr->nsegs, + size, (caddr_t *)&(scr->buf_va), busdma_flag | BUS_DMA_COHERENT); + if (error != 0) + goto bad; + + memset(scr->buf_va, 0, scr->buf_size); + bcopy(splash, scr->buf_va, + sizeof (splash) > scr->buf_size ? scr->buf_size : sizeof (splash)); + + /* map memory for DMA */ + if (bus_dmamap_create(dma_tag, 1024 * 1024 * 2, 1, + 1024 * 1024 * 2, 0, busdma_flag, &scr->dma)) + goto bad; + error = bus_dmamap_load(dma_tag, scr->dma, + scr->buf_va, size, NULL, busdma_flag); + if (error != 0) { + goto bad; + } + + scr->map_size = size; /* used when unmap this. */ + + if (sc != NULL) { + LIST_INSERT_HEAD(&(sc->sc_screens), scr, link); + sc->sc_nscreens++; + } + + omdisplay_initialize(sc, geometry); + + return (0); + + bad: + if (scr->buf_va) + bus_dmamem_unmap(dma_tag, scr->buf_va, size); + if (scr->nsegs) + bus_dmamem_free(dma_tag, scr->segs, scr->nsegs); + return (error); +} +paddr_t +omdisplay_mmap(void *v, off_t offset, int prot) +{ + struct omdisplay_softc *sc = v; + struct omdisplay_screen *screen = sc->sc_active; /* ??? */ + + if ((offset & PAGE_MASK) != 0) + return (-1); + + if (screen == NULL) + return (-1); + + if (offset < 0 || + offset >= screen->rinfo.ri_stride * screen->rinfo.ri_height) + return (-1); + + return (bus_dmamem_mmap(sc->sc_dma_tag, screen->segs, screen->nsegs, + offset, prot, BUS_DMA_WAITOK | BUS_DMA_COHERENT)); +} + +void +omdisplay_free_screen(void *v, void *cookie) +{ + struct omdisplay_softc *sc = v; + struct rasops_info *ri = cookie; + struct omdisplay_screen *scr = ri->ri_hw; + + LIST_REMOVE(scr, link); + sc->sc_nscreens--; + if (scr == sc->sc_active) { + /* at first, we need to stop LCD DMA */ + sc->sc_active = NULL; + +#ifdef DEBUG + printf("lcd_free on active screen\n"); +#endif + + omdisplay_stop(sc); + } + + if (scr->buf_va) + bus_dmamem_unmap(sc->sc_dma_tag, scr->buf_va, scr->map_size); + + if (scr->nsegs > 0) + bus_dmamem_free(sc->sc_dma_tag, scr->segs, scr->nsegs); + + free(scr, M_DEVBUF, 0); +} + +int +omdisplay_load_font(void *v, void *emulcookie, struct wsdisplay_font *font) +{ + struct omdisplay_softc *sc = v; + struct omdisplay_screen *scr = sc->sc_active; + + if (scr == NULL) + return ENXIO; + + return rasops_load_font(scr->rinfo, emulcookie, font); +} + +int +omdisplay_list_font(void *v, struct wsdisplay_font *font) +{ + struct omdisplay_softc *sc = v; + struct omdisplay_screen *scr = sc->sc_active; + + if (scr == NULL) + return ENXIO; + + return rasops_list_font(scr->rinfo, font); +} + +void +omdisplay_start(struct omdisplay_softc *sc) +{ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_CONTROL, + DISPC_CONTROL_GPOUT0 | DISPC_CONTROL_GPOUT1 | + DISPC_CONTROL_TFTDATALINES_18 /*XXX 18? */ | + DISPC_CONTROL_STNTFT | + DISPC_CONTROL_GOLCD | + DISPC_CONTROL_LCDENABLE); +} + +void +omdisplay_stop(struct omdisplay_softc *sc) +{ + bus_space_write_4(sc->sc_iot, sc->sc_dcioh, DISPC_CONTROL, + bus_space_read_4(sc->sc_iot, sc->sc_dcioh, DISPC_CONTROL) & + ~(DISPC_CONTROL_DIGITALENABLE|DISPC_CONTROL_LCDENABLE)); + + /* XXX - wait for end of frame? */ +} + +int +omdisplay_intr(void *v) +{ + /* XXX */ + return 1; +} + diff --git a/omdog.c b/omdog.c new file mode 100644 index 0000000..d3d54a5 --- /dev/null +++ b/omdog.c @@ -0,0 +1,190 @@ +/* $OpenBSD: omdog.c,v 1.5 2014/12/10 12:27:56 mikeb Exp $ */ +/* + * Copyright (c) 2013 Federico G. Schwindt + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIDR 0x00 /* Identification Register */ +#define WCLR 0x24 /* Control Register */ +#define WCLR_PRE (1 << 5) +#define WCLR_PTV (0 << 2) +#define WCRR 0x28 /* Counter Register */ +#define WLDR 0x2c /* Load Register */ +#define WTGR 0x30 /* Trigger Register */ +#define WWPS 0x34 /* Write Posting Bits Reg. */ +#define WWPS_WSPR (1 << 4) +#define WWPS_WTGR (1 << 3) +#define WWPS_WLDR (1 << 2) +#define WWPS_WCRR (1 << 1) +#define WWPS_WCLR (1 << 0) +#define WSPR 0x48 /* Start/Stop Register */ + +#define OMDOG_VAL(secs) (0xffffffff - ((secs) * (32768 / (1 << 0))) + 1) + + +struct omdog_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + int sc_period; +}; + +struct omdog_softc *omdog_sc; + +void omdog_attach(struct device *, struct device *, void *); +int omdog_activate(struct device *, int); +void omdog_start(struct omdog_softc *); +void omdog_stop(struct omdog_softc *); +void omdog_sync(struct omdog_softc *); +int omdog_cb(void *, int); +void omdog_reset(void); + +struct cfattach omdog_ca = { + sizeof (struct omdog_softc), NULL, omdog_attach, NULL, omdog_activate +}; + +struct cfdriver omdog_cd = { + NULL, "omdog", DV_DULL +}; + +void +omdog_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct omdog_softc *sc = (struct omdog_softc *) self; + u_int32_t rev; + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + rev = bus_space_read_4(sc->sc_iot, sc->sc_ioh, WIDR); + + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + omdog_sc = sc; + + omdog_stop(sc); + +#ifndef SMALL_KERNEL + wdog_register(omdog_cb, sc); +#endif +} + +int +omdog_activate(struct device *self, int act) +{ + switch (act) { + case DVACT_POWERDOWN: +#ifndef SMALL_KERNEL + wdog_shutdown(self); +#endif + break; + } + + return (0); +} + +void +omdog_start(struct omdog_softc *sc) +{ + /* Write the enable sequence data BBBBh followed by 4444h */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WSPR, 0xbbbb); + omdog_sync(sc); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WSPR, 0x4444); + omdog_sync(sc); +} + +void +omdog_stop(struct omdog_softc *sc) +{ + /* Write the disable sequence data AAAAh followed by 5555h */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WSPR, 0xaaaa); + omdog_sync(sc); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WSPR, 0x5555); + omdog_sync(sc); +} + +void +omdog_sync(struct omdog_softc *sc) +{ + while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, WWPS) & + (WWPS_WSPR|WWPS_WTGR|WWPS_WLDR|WWPS_WCRR|WWPS_WCLR)) + delay(10); +} + +int +omdog_cb(void *self, int period) +{ + struct omdog_softc *sc = self; + + if (sc->sc_period != 0 && sc->sc_period != period) + omdog_stop(sc); + + if (period != 0) { + if (sc->sc_period != period) { + /* Set the prescaler */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WCLR, + (WCLR_PRE|WCLR_PTV)); + + /* Set the reload counter */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WLDR, + OMDOG_VAL(period)); + } + + omdog_sync(sc); + + /* Trigger the reload */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, WTGR, + ~bus_space_read_4(sc->sc_iot, sc->sc_ioh, WTGR)); + + if (sc->sc_period != period) + omdog_start(sc); + } + + sc->sc_period = period; + + return (period); +} + +void +omdog_reset(void) +{ + if (omdog_sc == NULL) + return; + + if (omdog_sc->sc_period != 0) + omdog_stop(omdog_sc); + + bus_space_write_4(omdog_sc->sc_iot, omdog_sc->sc_ioh, WCRR, + 0xffffff80); + + omdog_start(omdog_sc); + + delay(100000); +} diff --git a/omehci.c b/omehci.c new file mode 100644 index 0000000..1ee3a06 --- /dev/null +++ b/omehci.c @@ -0,0 +1,486 @@ +/* $OpenBSD: omehci.c,v 1.3 2014/05/19 13:11:31 mpi Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +void omehci_attach(struct device *, struct device *, void *); +int omehci_detach(struct device *, int); +int omehci_activate(struct device *, int); + +struct omehci_softc { + struct ehci_softc sc; + void *sc_ih; + bus_space_handle_t uhh_ioh; + bus_space_handle_t tll_ioh; + + uint32_t ehci_rev; + uint32_t tll_avail; + + uint32_t port_mode[OMAP_HS_USB_PORTS]; + uint32_t phy_reset[OMAP_HS_USB_PORTS]; + uint32_t reset_gpio_pin[OMAP_HS_USB_PORTS]; + + void (*early_init)(void); +}; + +int omehci_init(struct omehci_softc *); +void omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port); +void omehci_enable(struct omehci_softc *); +void omehci_disable(struct omehci_softc *); +void omehci_utmi_init(struct omehci_softc *sc, unsigned int en_mask); +void misc_setup(struct omehci_softc *sc); +void omehci_phy_reset(uint32_t on, uint32_t _delay); +void omehci_uhh_init(struct omehci_softc *sc); +void omehci_v4_early_init(void); + +struct cfattach omehci_ca = { + sizeof (struct omehci_softc), NULL, omehci_attach, + omehci_detach, omehci_activate +}; + +void +omehci_attach(struct device *parent, struct device *self, void *aux) +{ + struct omehci_softc *sc = (struct omehci_softc *)self; + struct armv7_attach_args *aa = aux; + usbd_status r; + char *devname = sc->sc.sc_bus.bdev.dv_xname; + uint32_t i; + + sc->sc.iot = aa->aa_iot; + sc->sc.sc_bus.dmatag = aa->aa_dmat; + sc->sc.sc_size = aa->aa_dev->mem[0].size; + + /* set defaults */ + for (i = 0; i < 3; i++) { + sc->phy_reset[i] = 0; + sc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; + sc->reset_gpio_pin[i] = -1; + } + + switch (board_id) + { + case BOARD_ID_OMAP4_PANDA: + sc->tll_avail = 0; + sc->port_mode[0] = EHCI_HCD_OMAP_MODE_PHY; + sc->early_init = omehci_v4_early_init; + break; + default: + break; + } + + /* Map I/O space */ + if (bus_space_map(sc->sc.iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc.ioh)) { + printf(": cannot map mem space\n"); + goto out; + } + + if (bus_space_map(sc->sc.iot, aa->aa_dev->mem[1].addr, + aa->aa_dev->mem[1].size, 0, &sc->uhh_ioh)) { + printf(": cannot map mem space\n"); + goto mem0; + } + + if (sc->tll_avail && + bus_space_map(sc->sc.iot, aa->aa_dev->mem[2].addr, + aa->aa_dev->mem[2].size, 0, &sc->tll_ioh)) { + printf(": cannot map mem space\n"); + goto mem1; + } + + printf("\n"); + + if (sc->early_init) + sc->early_init(); + + if (omehci_init(sc)) + return; + + /* Disable interrupts, so we don't get any spurious ones. */ + sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); + EOWRITE2(&sc->sc, EHCI_USBINTR, 0); + + sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_USB, + ehci_intr, &sc->sc, devname); + if (sc->sc_ih == NULL) { + printf(": unable to establish interrupt\n"); + printf("XXX - disable ehci and prcm"); + goto mem2; + } + + strlcpy(sc->sc.sc_vendor, "TI OMAP", sizeof(sc->sc.sc_vendor)); + r = ehci_init(&sc->sc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: init failed, error=%d\n", devname, r); + printf("XXX - disable ehci and prcm"); + goto intr; + } + + config_found(self, &sc->sc.sc_bus, usbctlprint); + + goto out; + +intr: + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; +mem2: + bus_space_unmap(sc->sc.iot, sc->tll_ioh, aa->aa_dev->mem[2].size); +mem1: + bus_space_unmap(sc->sc.iot, sc->uhh_ioh, aa->aa_dev->mem[1].size); +mem0: + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; +out: + return; +} + +int +omehci_init(struct omehci_softc *sc) +{ + uint32_t i = 0, reg; + uint32_t reset_performed = 0; + uint32_t timeout = 0; + uint32_t tll_ch_mask = 0; + + /* enable high speed usb host clock */ + prcm_enablemodule(PRCM_USB); + + /* Hold the PHY in reset while configuring */ + for (i = 0; i < OMAP_HS_USB_PORTS; i++) { + if (sc->phy_reset[i]) { + /* Configure the GPIO to drive low (hold in reset) */ + if (sc->reset_gpio_pin[i] != -1) { + omgpio_set_dir(sc->reset_gpio_pin[i], + OMGPIO_DIR_OUT); + omgpio_clear_bit(sc->reset_gpio_pin[i]); + reset_performed = 1; + } + } + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + if (reset_performed) + delay(10); + + /* Read the UHH revision */ + sc->ehci_rev = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, + OMAP_USBHOST_UHH_REVISION); + + /* Initilise the low level interface module(s) */ + if (sc->ehci_rev == OMAP_EHCI_REV1) { + /* Enable the USB TLL */ + prcm_enablemodule(PRCM_USBTLL); + + /* Perform TLL soft reset, and wait until reset is complete */ + bus_space_write_4(sc->sc.iot, sc->tll_ioh, + 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 ((bus_space_read_4(sc->sc.iot, sc->tll_ioh, + OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE) + == 0x00) { + + /* Sleep for a tick */ + delay(10); + + if (timeout-- == 0) { + return 1; + } + } + + bus_space_write_4(sc->sc.iot, sc->tll_ioh, + OMAP_USBTLL_SYSCONFIG, + TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE | + TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY); + } else if (sc->ehci_rev == OMAP_EHCI_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 + */ + if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { + //ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); + prcm_enablemodule(PRCM_USBP1_PHY); + } else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + prcm_enablemodule(PRCM_USBP1_UTMI); + else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) + prcm_enablemodule(PRCM_USBP1_HSIC); + + if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { + //ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); + prcm_enablemodule(PRCM_USBP2_PHY); + } else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + prcm_enablemodule(PRCM_USBP2_UTMI); + else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) + prcm_enablemodule(PRCM_USBP2_HSIC); + } + + /* Put UHH in SmartIdle/SmartStandby mode */ + reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, + OMAP_USBHOST_UHH_SYSCONFIG); + if (sc->ehci_rev == OMAP_EHCI_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 (sc->ehci_rev == OMAP_EHCI_REV2) { + reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; + reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; + reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; + reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; + } + bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_SYSCONFIG, + reg); + + reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, + 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 (sc->ehci_rev == OMAP_EHCI_REV1) { + if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if ((sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || + (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || + (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) + reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; + else + reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; + } else if (sc->ehci_rev == OMAP_EHCI_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 (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; + else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) + reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; + + if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; + else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) + reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; + } + + bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_HOSTCONFIG, reg); + + /* If any of the ports are configured in TLL mode, enable them */ + for (i = 0; i < OMAP_HS_USB_PORTS; i++) + if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY) + tll_ch_mask |= 1 << i; + + /* Enable UTMI mode for required TLL channels */ +#ifdef notyet + if (tll_ch_mask) + omap_ehci_utmi_init(sc, tll_ch_mask); +#endif + + /* Release the PHY reset signal now we have configured everything */ + if (reset_performed) { + /* Delay for 10ms */ + delay(10000); + + /* Release reset */ + for (i = 0; i < 3; i++) { + if (sc->phy_reset[i] && (sc->reset_gpio_pin[i] != -1)) + omgpio_set_bit(sc->reset_gpio_pin[i]); + } + } + + /* 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 = bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD); + reg &= 0xff00ffff; + reg |= (1 << 16); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD, reg); + + /* Soft reset the PHY using PHY reset command over ULPI */ + for (i = 0; i < OMAP_HS_USB_PORTS; i++) + if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY) + omehci_soft_phy_reset(sc, i); + + return(0); +} + +void +omehci_soft_phy_reset(struct omehci_softc *sc, 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); + + bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI, reg); + + timeout += 1000000; + /* Wait for ULPI access completion */ + while ((bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI) + & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { + + /* Sleep for a tick */ + delay(10); + + if (timeout-- == 0) { + printf("PHY reset operation timed out\n"); + break; + } + } +} + +int +omehci_detach(struct device *self, int flags) +{ + struct omehci_softc *sc = (struct omehci_softc *)self; + int rv; + + rv = ehci_detach(self, flags); + if (rv) + return (rv); + + if (sc->sc_ih != NULL) { + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; + } + + if (sc->sc.sc_size) { + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; + } + + /* XXX: stop clock */ + + return (0); +} + +int +omehci_activate(struct device *self, int act) +{ + struct omehci_softc *sc = (struct omehci_softc *)self; + + switch (act) { + case DVACT_SUSPEND: + sc->sc.sc_bus.use_polling++; + /* FIXME */ + sc->sc.sc_bus.use_polling--; + break; + case DVACT_RESUME: + sc->sc.sc_bus.use_polling++; + /* FIXME */ + sc->sc.sc_bus.use_polling--; + break; + case DVACT_POWERDOWN: + ehci_reset(&sc->sc); + break; + } + return 0; +} + +void +omehci_v4_early_init() +{ + omgpio_set_dir(1, OMGPIO_DIR_OUT); + omgpio_clear_bit(1); + omgpio_set_dir(62, OMGPIO_DIR_OUT); + omgpio_clear_bit(62); + + /* wait for power down */ + delay(1000); + + omgpio_set_bit(1); + omgpio_set_bit(62); + + /* wait until powered up */ + delay(1000); +} diff --git a/omehcivar.h b/omehcivar.h new file mode 100644 index 0000000..8eed72d --- /dev/null +++ b/omehcivar.h @@ -0,0 +1,232 @@ +/* $OpenBSD: omehcivar.h,v 1.1 2013/09/04 14:38:31 patrick Exp $ */ + +/* + * Misc + */ +#define OMAP_HS_USB_PORTS 3 + +/* + * USB TTL 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))) + + +/* + * 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 + +/* 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 + + + + + +/* 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) + + +/* 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) + +#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 + +/* + * 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_EHCI_REV1 0x00000010 /* OMAP3 */ +#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */ + +#define EHCI_VENDORID_OMAP3 0x42fa05 +#define OMAP_EHCI_HC_DEVSTR "TI OMAP USB 2.0 controller" + +#define EHCI_HCD_OMAP_MODE_UNKNOWN 0 +#define EHCI_HCD_OMAP_MODE_PHY 1 +#define EHCI_HCD_OMAP_MODE_TLL 2 +#define EHCI_HCD_OMAP_MODE_HSIC 3 diff --git a/omgpio.c b/omgpio.c new file mode 100644 index 0000000..5df65bd --- /dev/null +++ b/omgpio.c @@ -0,0 +1,763 @@ +/* $OpenBSD: omgpio.c,v 1.6 2016/01/31 00:14:50 jsg Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "gpio.h" + +/* OMAP3 registers */ +#define GPIO3_REVISION 0x00 +#define GPIO3_SYSCONFIG 0x10 +#define GPIO3_SYSSTATUS 0x14 +#define GPIO3_IRQSTATUS1 0x18 +#define GPIO3_IRQENABLE1 0x1C +#define GPIO3_WAKEUPENABLE 0x20 +#define GPIO3_IRQSTATUS2 0x28 +#define GPIO3_IRQENABLE2 0x2C +#define GPIO3_CTRL 0x30 +#define GPIO3_OE 0x34 +#define GPIO3_DATAIN 0x38 +#define GPIO3_DATAOUT 0x3C +#define GPIO3_LEVELDETECT0 0x40 +#define GPIO3_LEVELDETECT1 0x44 +#define GPIO3_RISINGDETECT 0x48 +#define GPIO3_FALLINGDETECT 0x4C +#define GPIO3_DEBOUNCENABLE 0x50 +#define GPIO3_DEBOUNCINGTIME 0x54 +#define GPIO3_CLEARIRQENABLE1 0x60 +#define GPIO3_SETIRQENABLE1 0x64 +#define GPIO3_CLEARIRQENABLE2 0x70 +#define GPIO3_SETIRQENABLE2 0x74 +#define GPIO3_CLEARWKUENA 0x80 +#define GPIO3_SETWKUENA 0x84 +#define GPIO3_CLEARDATAOUT 0x90 +#define GPIO3_SETDATAOUT 0x94 + +/* OMAP4 registers */ +#define GPIO4_REVISION 0x00 +#define GPIO4_SYSCONFIG 0x10 +#define GPIO4_IRQSTATUS_RAW_0 0x24 +#define GPIO4_IRQSTATUS_RAW_1 0x28 +#define GPIO4_IRQSTATUS_0 0x2C +#define GPIO4_IRQSTATUS_1 0x30 +#define GPIO4_IRQSTATUS_SET_0 0x34 +#define GPIO4_IRQSTATUS_SET_1 0x38 +#define GPIO4_IRQSTATUS_CLR_0 0x3C +#define GPIO4_IRQSTATUS_CLR_1 0x40 +#define GPIO4_IRQWAKEN_0 0x44 +#define GPIO4_IRQWAKEN_1 0x48 +#define GPIO4_SYSSTATUS 0x114 +#define GPIO4_WAKEUPENABLE 0x120 +#define GPIO4_CTRL 0x130 +#define GPIO4_OE 0x134 +#define GPIO4_DATAIN 0x138 +#define GPIO4_DATAOUT 0x13C +#define GPIO4_LEVELDETECT0 0x140 +#define GPIO4_LEVELDETECT1 0x144 +#define GPIO4_RISINGDETECT 0x148 +#define GPIO4_FALLINGDETECT 0x14C +#define GPIO4_DEBOUNCENABLE 0x150 +#define GPIO4_DEBOUNCINGTIME 0x154 +#define GPIO4_CLEARWKUPENA 0x180 +#define GPIO4_SETWKUENA 0x184 +#define GPIO4_CLEARDATAOUT 0x190 +#define GPIO4_SETDATAOUT 0x194 + +/* AM335X registers */ +#define GPIO_AM335X_REVISION 0x00 +#define GPIO_AM335X_SYSCONFIG 0x10 +#define GPIO_AM335X_IRQSTATUS_RAW_0 0x24 +#define GPIO_AM335X_IRQSTATUS_RAW_1 0x28 +#define GPIO_AM335X_IRQSTATUS_0 0x2C +#define GPIO_AM335X_IRQSTATUS_1 0x30 +#define GPIO_AM335X_IRQSTATUS_SET_0 0x34 +#define GPIO_AM335X_IRQSTATUS_SET_1 0x38 +#define GPIO_AM335X_IRQSTATUS_CLR_0 0x3c +#define GPIO_AM335X_IRQSTATUS_CLR_1 0x40 +#define GPIO_AM335X_IRQWAKEN_0 0x44 +#define GPIO_AM335X_IRQWAKEN_1 0x48 +#define GPIO_AM335X_SYSSTATUS 0x114 +#define GPIO_AM335X_CTRL 0x130 +#define GPIO_AM335X_OE 0x134 +#define GPIO_AM335X_DATAIN 0x138 +#define GPIO_AM335X_DATAOUT 0x13C +#define GPIO_AM335X_LEVELDETECT0 0x140 +#define GPIO_AM335X_LEVELDETECT1 0x144 +#define GPIO_AM335X_RISINGDETECT 0x148 +#define GPIO_AM335X_FALLINGDETECT 0x14C +#define GPIO_AM335X_DEBOUNCENABLE 0x150 +#define GPIO_AM335X_DEBOUNCINGTIME 0x154 +#define GPIO_AM335X_CLEARDATAOUT 0x190 +#define GPIO_AM335X_SETDATAOUT 0x194 + +#define GPIO_NUM_PINS 32 + +struct intrhand { + int (*ih_func)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_ipl; /* IPL_* */ + int ih_irq; /* IRQ number */ + int ih_gpio; /* gpio pin */ + struct evcount ih_count; + char *ih_name; +}; + +struct omgpio_regs { + u_int32_t revision; + u_int32_t sysconfig; + u_int32_t irqstatus_raw0; /* omap4/am335x only */ + u_int32_t irqstatus_raw1; /* omap4/am335x only */ + u_int32_t irqstatus0; + u_int32_t irqstatus1; + u_int32_t irqstatus_set0; + u_int32_t irqstatus_set1; + u_int32_t irqstatus_clear0; + u_int32_t irqstatus_clear1; + u_int32_t irqwaken0; + u_int32_t irqwaken1; + u_int32_t sysstatus; + u_int32_t wakeupenable; /* omap3/omap4 only */ + u_int32_t ctrl; + u_int32_t oe; + u_int32_t datain; + u_int32_t dataout; + u_int32_t leveldetect0; + u_int32_t leveldetect1; + u_int32_t risingdetect; + u_int32_t fallingdetect; + u_int32_t debounceenable; + u_int32_t debouncingtime; + u_int32_t clearwkupena; /* omap3/omap4 only */ + u_int32_t setwkupena; /* omap3/omap4 only */ + u_int32_t cleardataout; + u_int32_t setdataout; +}; + +struct omgpio_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + void *sc_ih_h; + void *sc_ih_l; + int sc_max_il; + int sc_min_il; + int sc_irq; + struct intrhand *sc_handlers[GPIO_NUM_PINS]; + int sc_omap_ver; + struct gpio_chipset_tag sc_gpio_gc; + gpio_pin_t sc_gpio_pins[GPIO_NUM_PINS]; + struct omgpio_regs sc_regs; +}; + +#define GPIO_PIN_TO_INST(x) ((x) >> 5) +#define GPIO_PIN_TO_OFFSET(x) ((x) & 0x1f) +#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) +#define READ4(sc, reg) omgpio_read4(sc, reg) +#define WRITE4(sc, reg, val) omgpio_write4(sc, reg, val) + +u_int32_t omgpio_read4(struct omgpio_softc *, u_int32_t); +void omgpio_write4(struct omgpio_softc *, u_int32_t, u_int32_t); +int omgpio_match(struct device *, void *, void *); +void omgpio_attach(struct device *, struct device *, void *); +void omgpio_recalc_interrupts(struct omgpio_softc *); +int omgpio_irq(void *); +int omgpio_irq_dummy(void *); +int omgpio_pin_dir_read(struct omgpio_softc *, unsigned int); +void omgpio_pin_dir_write(struct omgpio_softc *, unsigned int, unsigned int); + +struct cfattach omgpio_ca = { + sizeof (struct omgpio_softc), omgpio_match, omgpio_attach +}; + +struct cfdriver omgpio_cd = { + NULL, "omgpio", DV_DULL +}; + +u_int32_t +omgpio_read4(struct omgpio_softc *sc, u_int32_t reg) +{ + if(reg == -1) + panic("%s: Invalid register address", DEVNAME(sc)); + + return bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)); +} + +void +omgpio_write4(struct omgpio_softc *sc, u_int32_t reg, u_int32_t val) +{ + if(reg == -1) + panic("%s: Invalid register address", DEVNAME(sc)); + + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)); +} + +int +omgpio_match(struct device *parent, void *v, void *aux) +{ + switch (board_id) { + case BOARD_ID_OMAP3_BEAGLE: + case BOARD_ID_OMAP3_OVERO: + case BOARD_ID_AM335X_BEAGLEBONE: + case BOARD_ID_OMAP4_PANDA: + break; /* continue trying */ + default: + return 0; /* unknown */ + } + return (1); +} + +void +omgpio_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct omgpio_softc *sc = (struct omgpio_softc *) self; + struct gpiobus_attach_args gba; + u_int32_t rev; + int i; + + prcm_enablemodule(PRCM_GPIO0 + aa->aa_dev->unit); + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", DEVNAME(sc)); + + switch (board_id) { + case BOARD_ID_OMAP3_BEAGLE: + case BOARD_ID_OMAP3_OVERO: + sc->sc_regs.revision = GPIO3_REVISION; + sc->sc_regs.sysconfig = GPIO3_SYSCONFIG; + sc->sc_regs.irqstatus_raw0 = -1; + sc->sc_regs.irqstatus_raw1 = -1; + sc->sc_regs.irqstatus0 = GPIO3_IRQSTATUS1; + sc->sc_regs.irqstatus1 = GPIO3_IRQSTATUS2; + sc->sc_regs.irqstatus_set0 = GPIO3_SETIRQENABLE1; + sc->sc_regs.irqstatus_set1 = GPIO3_SETIRQENABLE2; + sc->sc_regs.irqstatus_clear0 = GPIO3_CLEARIRQENABLE1; + sc->sc_regs.irqstatus_clear1 = GPIO3_CLEARIRQENABLE2; + sc->sc_regs.irqwaken0 = -1; + sc->sc_regs.irqwaken1 = -1; + sc->sc_regs.sysstatus = GPIO3_SYSSTATUS; + sc->sc_regs.wakeupenable = GPIO3_WAKEUPENABLE; + sc->sc_regs.ctrl = GPIO3_CTRL; + sc->sc_regs.oe = GPIO3_OE; + sc->sc_regs.datain = GPIO3_DATAIN; + sc->sc_regs.dataout = GPIO3_DATAOUT; + sc->sc_regs.leveldetect0 = GPIO3_LEVELDETECT0; + sc->sc_regs.leveldetect1 = GPIO3_LEVELDETECT1; + sc->sc_regs.risingdetect = GPIO3_RISINGDETECT; + sc->sc_regs.fallingdetect = GPIO3_FALLINGDETECT; + sc->sc_regs.debounceenable = GPIO3_DEBOUNCENABLE; + sc->sc_regs.debouncingtime = GPIO3_DEBOUNCINGTIME; + sc->sc_regs.clearwkupena = GPIO3_CLEARWKUENA; + sc->sc_regs.setwkupena = GPIO3_SETWKUENA; + sc->sc_regs.cleardataout = GPIO3_CLEARDATAOUT; + sc->sc_regs.setdataout = GPIO3_SETDATAOUT; + break; + case BOARD_ID_OMAP4_PANDA: + sc->sc_regs.revision = GPIO4_REVISION; + sc->sc_regs.sysconfig = GPIO4_SYSCONFIG; + sc->sc_regs.irqstatus_raw0 = GPIO4_IRQSTATUS_RAW_0; + sc->sc_regs.irqstatus_raw1 = GPIO4_IRQSTATUS_RAW_1; + sc->sc_regs.irqstatus0 = GPIO4_IRQSTATUS_0; + sc->sc_regs.irqstatus1 = GPIO4_IRQSTATUS_1; + sc->sc_regs.irqstatus_set0 = GPIO4_IRQSTATUS_SET_0; + sc->sc_regs.irqstatus_set1 = GPIO4_IRQSTATUS_SET_1; + sc->sc_regs.irqstatus_clear0 = GPIO4_IRQSTATUS_CLR_0; + sc->sc_regs.irqstatus_clear1 = GPIO4_IRQSTATUS_CLR_1; + sc->sc_regs.irqwaken0 = GPIO4_IRQWAKEN_0; + sc->sc_regs.irqwaken1 = GPIO4_IRQWAKEN_1; + sc->sc_regs.sysstatus = GPIO4_SYSSTATUS; + sc->sc_regs.wakeupenable = GPIO4_WAKEUPENABLE; + sc->sc_regs.ctrl = GPIO4_CTRL; + sc->sc_regs.oe = GPIO4_OE; + sc->sc_regs.datain = GPIO4_DATAIN; + sc->sc_regs.dataout = GPIO4_DATAOUT; + sc->sc_regs.leveldetect0 = GPIO4_LEVELDETECT0; + sc->sc_regs.leveldetect1 = GPIO4_LEVELDETECT1; + sc->sc_regs.risingdetect = GPIO4_RISINGDETECT; + sc->sc_regs.fallingdetect = GPIO4_FALLINGDETECT; + sc->sc_regs.debounceenable = GPIO4_DEBOUNCENABLE; + sc->sc_regs.debouncingtime = GPIO4_DEBOUNCINGTIME; + sc->sc_regs.clearwkupena = GPIO4_CLEARWKUPENA; + sc->sc_regs.setwkupena = GPIO4_SETWKUENA; + sc->sc_regs.cleardataout = GPIO4_CLEARDATAOUT; + sc->sc_regs.setdataout = GPIO4_SETDATAOUT; + break; + case BOARD_ID_AM335X_BEAGLEBONE: + sc->sc_regs.revision = GPIO_AM335X_REVISION; + sc->sc_regs.sysconfig = GPIO_AM335X_SYSCONFIG; + sc->sc_regs.irqstatus_raw0 = GPIO_AM335X_IRQSTATUS_RAW_0; + sc->sc_regs.irqstatus_raw1 = GPIO_AM335X_IRQSTATUS_RAW_1; + sc->sc_regs.irqstatus0 = GPIO_AM335X_IRQSTATUS_0; + sc->sc_regs.irqstatus1 = GPIO_AM335X_IRQSTATUS_1; + sc->sc_regs.irqstatus_set0 = GPIO_AM335X_IRQSTATUS_SET_0; + sc->sc_regs.irqstatus_set1 = GPIO_AM335X_IRQSTATUS_SET_1; + sc->sc_regs.irqstatus_clear0 = GPIO_AM335X_IRQSTATUS_CLR_0; + sc->sc_regs.irqstatus_clear1 = GPIO_AM335X_IRQSTATUS_CLR_1; + sc->sc_regs.irqwaken0 = GPIO_AM335X_IRQWAKEN_0; + sc->sc_regs.irqwaken1 = GPIO_AM335X_IRQWAKEN_1; + sc->sc_regs.sysstatus = GPIO_AM335X_SYSSTATUS; + sc->sc_regs.wakeupenable = -1; + sc->sc_regs.ctrl = GPIO_AM335X_CTRL; + sc->sc_regs.oe = GPIO_AM335X_OE; + sc->sc_regs.datain = GPIO_AM335X_DATAIN; + sc->sc_regs.dataout = GPIO_AM335X_DATAOUT; + sc->sc_regs.leveldetect0 = GPIO_AM335X_LEVELDETECT0; + sc->sc_regs.leveldetect1 = GPIO_AM335X_LEVELDETECT1; + sc->sc_regs.risingdetect = GPIO_AM335X_RISINGDETECT; + sc->sc_regs.fallingdetect = GPIO_AM335X_FALLINGDETECT; + sc->sc_regs.debounceenable = GPIO_AM335X_DEBOUNCENABLE; + sc->sc_regs.debouncingtime = GPIO_AM335X_DEBOUNCINGTIME; + sc->sc_regs.clearwkupena = -1; + sc->sc_regs.setwkupena = -1; + sc->sc_regs.cleardataout = GPIO_AM335X_CLEARDATAOUT; + sc->sc_regs.setdataout = GPIO_AM335X_SETDATAOUT; + break; + } + + rev = READ4(sc, sc->sc_regs.revision); + + printf(": rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + + sc->sc_irq = aa->aa_dev->irq[0]; + + WRITE4(sc, sc->sc_regs.irqstatus_clear0, ~0); + WRITE4(sc, sc->sc_regs.irqstatus_clear1, ~0); + + /* XXX - SYSCONFIG */ + /* XXX - CTRL */ + /* XXX - DEBOUNCE */ + + for (i = 0; i < GPIO_NUM_PINS; i++) { + sc->sc_gpio_pins[i].pin_num = i; + sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | + GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN; + sc->sc_gpio_pins[i].pin_state = omgpio_pin_read(sc, i) ? + GPIO_PIN_HIGH : GPIO_PIN_LOW; + sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_SET; + } + + sc->sc_gpio_gc.gp_cookie = sc; + sc->sc_gpio_gc.gp_pin_read = omgpio_pin_read; + sc->sc_gpio_gc.gp_pin_write = omgpio_pin_write; + sc->sc_gpio_gc.gp_pin_ctl = omgpio_pin_ctl; + + gba.gba_name = "gpio"; + gba.gba_gc = &sc->sc_gpio_gc; + gba.gba_pins = sc->sc_gpio_pins; + gba.gba_npins = GPIO_NUM_PINS; + +#if NGPIO > 0 + config_found(&sc->sc_dev, &gba, gpiobus_print); +#endif +} + +/* XXX - This assumes MCU INTERRUPTS are IRQ1, and DSP are IRQ2 */ + +#if 0 +/* XXX - FIND THESE REGISTERS !!! */ +unsigned int +omgpio_get_function(unsigned int gpio, unsigned int fn) +{ + return 0; +} + +void +omgpio_set_function(unsigned int gpio, unsigned int fn) +{ +} +#endif + +unsigned int +omgpio_get_bit(unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + return omgpio_pin_read(sc, GPIO_PIN_TO_OFFSET(gpio)); +} + +void +omgpio_set_bit(unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + omgpio_pin_write(sc, GPIO_PIN_TO_OFFSET(gpio), GPIO_PIN_HIGH); +} + +void +omgpio_clear_bit(unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + omgpio_pin_write(sc, GPIO_PIN_TO_OFFSET(gpio), GPIO_PIN_LOW); +} + +void +omgpio_set_dir(unsigned int gpio, unsigned int dir) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + omgpio_pin_dir_write(sc, GPIO_PIN_TO_OFFSET(gpio), dir); +} + +int +omgpio_pin_read(void *arg, int pin) +{ + struct omgpio_softc *sc = arg; + u_int32_t reg; + + if(omgpio_pin_dir_read(sc, pin) == OMGPIO_DIR_IN) + reg = READ4(sc, sc->sc_regs.datain); + else + reg = READ4(sc, sc->sc_regs.dataout); + return (reg >> GPIO_PIN_TO_OFFSET(pin)) & 0x1; +} + +void +omgpio_pin_write(void *arg, int pin, int value) +{ + struct omgpio_softc *sc = arg; + + if (value) + WRITE4(sc, sc->sc_regs.setdataout, + 1 << GPIO_PIN_TO_OFFSET(pin)); + else + WRITE4(sc, sc->sc_regs.cleardataout, + 1 << GPIO_PIN_TO_OFFSET(pin)); +} + +void +omgpio_pin_ctl(void *arg, int pin, int flags) +{ + struct omgpio_softc *sc = arg; + + if (flags & GPIO_PIN_INPUT) + omgpio_pin_dir_write(sc, pin, OMGPIO_DIR_IN); + else if (flags & GPIO_PIN_OUTPUT) + omgpio_pin_dir_write(sc, pin, OMGPIO_DIR_OUT); + + if (board_id == BOARD_ID_AM335X_BEAGLEBONE) + sitara_cm_padconf_set_gpioflags( + sc->sc_dev.dv_unit * GPIO_NUM_PINS + pin, flags); +} + +void +omgpio_pin_dir_write(struct omgpio_softc *sc, unsigned int gpio, + unsigned int dir) +{ + int s; + u_int32_t reg; + + s = splhigh(); + + reg = READ4(sc, sc->sc_regs.oe); + if (dir == OMGPIO_DIR_IN) + reg |= 1 << GPIO_PIN_TO_OFFSET(gpio); + else + reg &= ~(1 << GPIO_PIN_TO_OFFSET(gpio)); + WRITE4(sc, sc->sc_regs.oe, reg); + + splx(s); +} + +int +omgpio_pin_dir_read(struct omgpio_softc *sc, unsigned int gpio) +{ + u_int32_t reg; + reg = READ4(sc, sc->sc_regs.oe); + if (reg & (1 << GPIO_PIN_TO_OFFSET(gpio))) + return OMGPIO_DIR_IN; + else + return OMGPIO_DIR_OUT; +} + +#if 0 +void +omgpio_clear_intr(struct omgpio_softc *sc, unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + WRITE4(sc, sc->sc_regs.irqstatus0, 1 << GPIO_PIN_TO_OFFSET(gpio)); +} + +void +omgpio_intr_mask(struct omgpio_softc *sc, unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + WRITE4(sc, sc->sc_regs.irqstatus_clear0, 1 << GPIO_PIN_TO_OFFSET(gpio)); +} + +void +omgpio_intr_unmask(struct omgpio_softc *sc, unsigned int gpio) +{ + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + WRITE4(sc, sc->sc_regs.irqstatus_set0, 1 << GPIO_PIN_TO_OFFSET(gpio)); +} + +void +omgpio_intr_level(struct omgpio_softc *sc, unsigned int gpio, unsigned int level) +{ + u_int32_t fe, re, l0, l1, bit; + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + int s; + + s = splhigh(); + + fe = READ4(sc, sc->sc_regs.fallingdetect); + re = READ4(sc, sc->sc_regs.risingdetect); + l0 = READ4(sc, sc->sc_regs.leveldetect0); + l1 = READ4(sc, sc->sc_regs.leveldetect1); + + bit = 1 << GPIO_PIN_TO_OFFSET(gpio); + + switch (level) { + case IST_NONE: + fe &= ~bit; + re &= ~bit; + l0 &= ~bit; + l1 &= ~bit; + break; + case IST_EDGE_FALLING: + fe |= bit; + re &= ~bit; + l0 &= ~bit; + l1 &= ~bit; + break; + case IST_EDGE_RISING: + fe &= ~bit; + re |= bit; + l0 &= ~bit; + l1 &= ~bit; + break; + case IST_PULSE: /* XXX */ + /* FALLTHRU */ + case IST_EDGE_BOTH: + fe |= bit; + re |= bit; + l0 &= ~bit; + l1 &= ~bit; + break; + case IST_LEVEL_LOW: + fe &= ~bit; + re &= ~bit; + l0 |= bit; + l1 &= ~bit; + break; + case IST_LEVEL_HIGH: + fe &= ~bit; + re &= ~bit; + l0 &= ~bit; + l1 |= bit; + break; + default: + panic("omgpio_intr_level: bad level: %d", level); + break; + } + + WRITE4(sc, sc->sc_regs.fallingdetect, fe); + WRITE4(sc, sc->sc_regs.risingdetect, re); + WRITE4(sc, sc->sc_regs.leveldetect0, l0); + WRITE4(sc, sc->sc_regs.leveldetect1, l1); + + splx(s); +} + +void * +omgpio_intr_establish(struct omgpio_softc *sc, unsigned int gpio, int level, int spl, + int (*func)(void *), void *arg, char *name) +{ + int psw; + struct intrhand *ih; + struct omgpio_softc *sc; + + /* + * XXX - is gpio here the pin or the interrupt number + * which is 96 + gpio pin? + */ + + if (GPIO_PIN_TO_INST(gpio) > omgpio_cd.cd_ndevs) + panic("omgpio_intr_establish: bogus irqnumber %d: %s", + gpio, name); + + sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + if (sc->sc_handlers[GPIO_PIN_TO_OFFSET(gpio)] != NULL) + panic("omgpio_intr_establish: gpio pin busy %d old %s new %s", + gpio, sc->sc_handlers[GPIO_PIN_TO_OFFSET(gpio)]->ih_name, + name); + + psw = disable_interrupts(PSR_I); + + /* no point in sleeping unless someone can free memory. */ + ih = (struct intrhand *)malloc( sizeof *ih, M_DEVBUF, + cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + ih->ih_func = func; + ih->ih_arg = arg; + ih->ih_ipl = level; + ih->ih_gpio = gpio; + ih->ih_irq = gpio + 512; + ih->ih_name = name; + + sc->sc_handlers[GPIO_PIN_TO_OFFSET(gpio)] = ih; + + evcount_attach(&ih->ih_count, name, &ih->ih_irq); + + omgpio_intr_level(gpio, level); + omgpio_intr_unmask(gpio); + + omgpio_recalc_interrupts(sc); + + restore_interrupts(psw); + + return (ih); +} + +void +omgpio_intr_disestablish(struct omgpio_softc *sc, void *cookie) +{ + int psw; + struct intrhand *ih = cookie; + struct omgpio_softc *sc = omgpio_cd.cd_devs[GPIO_PIN_TO_INST(ih->ih_gpio)]; + int gpio = ih->ih_gpio; + psw = disable_interrupts(PSR_I); + + ih = sc->sc_handlers[GPIO_PIN_TO_OFFSET(gpio)]; + sc->sc_handlers[GPIO_PIN_TO_OFFSET(gpio)] = NULL; + + evcount_detach(&ih->ih_count); + + free(ih, M_DEVBUF, 0); + + omgpio_intr_level(gpio, IST_NONE); + omgpio_intr_mask(gpio); + omgpio_clear_intr(gpio); /* Just in case */ + + omgpio_recalc_interrupts(sc); + + restore_interrupts(psw); +} + +int +omgpio_irq(void *v) +{ + struct omgpio_softc *sc = v; + u_int32_t pending; + struct intrhand *ih; + int bit; + + pending = READ4(sc, omgpio.irqstatus0); + + while (pending != 0) { + bit = ffs(pending) - 1; + ih = sc->sc_handlers[bit]; + + if (ih != NULL) { + if (ih->ih_func(ih->ih_arg)) + ih->ih_count.ec_count++; + omgpio_clear_intr(ih->ih_gpio); + } else { + panic("omgpio: irq fired no handler, gpio %x %x %x", + sc->sc_dev.dv_unit * 32 + bit, pending, + READ4(sc, omgpio.irqstatus0) + + ); + } + pending &= ~(1 << bit); + } + return 1; +} + +int +omgpio_irq_dummy(void *v) +{ + return 0; +} + +void +omgpio_recalc_interrupts(struct omgpio_softc *sc) +{ + struct intrhand *ih; + int max = IPL_NONE; + int min = IPL_HIGH; + int i; + + for (i = 0; i < GPIO_NUM_PINS; i++) { + ih = sc->sc_handlers[i]; + if (ih != NULL) { + if (ih->ih_ipl > max) + max = ih->ih_ipl; + + if (ih->ih_ipl < min) + min = ih->ih_ipl; + } + } + if (max == IPL_NONE) + min = IPL_NONE; + +#if 0 + if ((max == IPL_NONE || max != sc->sc_max_il) && sc->sc_ih_h != NULL) + arm_intr_disestablish(sc->sc_ih_h); + + if (max != IPL_NONE && max != sc->sc_max_il) { + sc->sc_ih_h = arm_intr_establish(sc->sc_irq, max, omgpio_irq, + sc, NULL); + } +#else + if (sc->sc_ih_h != NULL) + arm_intr_disestablish(sc->sc_ih_h); + + if (max != IPL_NONE) { + sc->sc_ih_h = arm_intr_establish(sc->sc_irq, max, omgpio_irq, + sc, NULL); + } +#endif + + sc->sc_max_il = max; + + if (sc->sc_ih_l != NULL) + arm_intr_disestablish(sc->sc_ih_l); + + if (max != min) { + sc->sc_ih_h = arm_intr_establish(sc->sc_irq, min, + omgpio_irq_dummy, sc, NULL); + } + sc->sc_min_il = min; +} +#endif diff --git a/omgpiovar.h b/omgpiovar.h new file mode 100644 index 0000000..1c69f1b --- /dev/null +++ b/omgpiovar.h @@ -0,0 +1,44 @@ +/* $OpenBSD: omgpiovar.h,v 1.2 2013/11/20 13:32:40 rapha Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef OMGPIOVAR_H +#define OMGPIOVAR_H + +#define OMGPIO_DIR_IN 0 +#define OMGPIO_DIR_OUT 1 + +unsigned int omgpio_get_function(unsigned int gpio, unsigned int fn); +void omgpio_set_function(unsigned int gpio, unsigned int fn); +unsigned int omgpio_get_bit(unsigned int gpio); +void omgpio_set_bit(unsigned int gpio); +void omgpio_clear_bit(unsigned int gpio); +void omgpio_set_dir(unsigned int gpio, unsigned int dir); + +int omgpio_pin_read(void *arg, int pin); +void omgpio_pin_write(void *arg, int pin, int value); +void omgpio_pin_ctl(void *arg, int pin, int flags); + +/* interrupts */ +void omgpio_clear_intr(unsigned int gpio); +void omgpio_intr_mask(unsigned int gpio); +void omgpio_intr_unmask(unsigned int gpio); +void omgpio_intr_level(unsigned int gpio, unsigned int level); +void *omgpio_intr_establish(unsigned int gpio, int level, int spl, + int (*func)(void *), void *arg, char *name); +void omgpio_intr_disestablish(void *cookie); + +#endif /* OMGPIOVAR_H */ diff --git a/ommmc.c b/ommmc.c new file mode 100644 index 0000000..92a585b --- /dev/null +++ b/ommmc.c @@ -0,0 +1,1153 @@ +/* $OpenBSD: ommmc.c,v 1.18 2016/05/02 07:38:34 jsg Exp $ */ + +/* + * Copyright (c) 2009 Dale Rahn + * Copyright (c) 2006 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Omap SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * NOTE: on OMAP4430/AM335x these registers skew by 0x100 + * this is handled by mapping at base address + 0x100 + */ +/* registers */ +#define MMCHS_SYSCONFIG 0x010 +#define MMCHS_SYSSTATUS 0x014 +#define MMCHS_CSRE 0x024 +#define MMCHS_SYSTEST 0x028 +#define MMCHS_SYSTEST_SDCD (1 << 15) +#define MMCHS_CON 0x02C +#define MMCHS_CON_INIT (1<<1) +#define MMCHS_CON_DW8 (1<<5) +#define MMCHS_CON_OD (1<<0) +#define MMCHS_PWCNT 0x030 +#define MMCHS_BLK 0x104 +#define MMCHS_BLK_NBLK_MAX 0xffff +#define MMCHS_BLK_NBLK_SHIFT 16 +#define MMCHS_BLK_NBLK_MASK (MMCHS_BLK_NBLK_MAX<sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +int ommmc_host_reset(sdmmc_chipset_handle_t); +uint32_t ommmc_host_ocr(sdmmc_chipset_handle_t); +int ommmc_host_maxblklen(sdmmc_chipset_handle_t); +int ommmc_card_detect(sdmmc_chipset_handle_t); +int ommmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int ommmc_bus_clock(sdmmc_chipset_handle_t, int); +int ommmc_bus_width(sdmmc_chipset_handle_t, int); +void ommmc_card_intr_mask(sdmmc_chipset_handle_t, int); +void ommmc_card_intr_ack(sdmmc_chipset_handle_t); +void ommmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); +int ommmc_start_command(struct ommmc_softc *, struct sdmmc_command *); +int ommmc_wait_state(struct ommmc_softc *, uint32_t, uint32_t); +int ommmc_soft_reset(struct ommmc_softc *, int); +int ommmc_wait_intr(struct ommmc_softc *, int, int); +void ommmc_transfer_data(struct ommmc_softc *, struct sdmmc_command *); +void ommmc_read_data(struct ommmc_softc *, uint8_t *, int); +void ommmc_write_data(struct ommmc_softc *, uint8_t *, int); + +/* #define SDHC_DEBUG */ +#ifdef SDHC_DEBUG +int ommmcdebug = 20; +#define DPRINTF(n,s) do { if ((n) <= ommmcdebug) printf s; } while (0) +void ommmc_dump_regs(struct ommmc_softc *); +#else +#define DPRINTF(n,s) do {} while(0) +#endif + +struct sdmmc_chip_functions ommmc_functions = { + /* host controller reset */ + ommmc_host_reset, + /* host controller capabilities */ + ommmc_host_ocr, + ommmc_host_maxblklen, + /* card detection */ + ommmc_card_detect, + /* bus power and clock frequency */ + ommmc_bus_power, + ommmc_bus_clock, + ommmc_bus_width, + /* command execution */ + ommmc_exec_command, + /* card interrupt */ + ommmc_card_intr_mask, + ommmc_card_intr_ack +}; + +struct cfdriver ommmc_cd = { + NULL, "ommmc", DV_DULL +}; + +struct cfattach ommmc_ca = { + sizeof(struct ommmc_softc), NULL, ommmc_attach +}; + +void +ommmc_attach(struct device *parent, struct device *self, void *args) +{ + struct ommmc_softc *sc = (struct ommmc_softc *) self; + struct armv7_attach_args *aa = args; + struct sdmmcbus_attach_args saa; + uint32_t caps; + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + /* Enable ICLKEN, FCLKEN? */ + prcm_enablemodule(PRCM_MMC0 + aa->aa_dev->unit); + + sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_SDMMC, + ommmc_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf("%s: cannot map interrupt\n", DEVNAME(sc)); + goto err; + } + + /* Controller Voltage Capabilities Initialization */ + HSET4(sc, MMCHS_CAPA, MMCHS_CAPA_VS18 | MMCHS_CAPA_VS30); + +#ifdef SDHC_DEBUG + ommmc_dump_regs(sc); +#endif + + /* + * Reset the host controller and enable interrupts. + */ + ommmc_host_reset(sc); + + /* Determine host capabilities. */ + caps = HREAD4(sc, MMCHS_CAPA); + +#if 0 + /* we want this !! */ + /* Use DMA if the host system and the controller support it. */ + if (usedma && ISSET(caps, SDHC_DMA_SUPPORT)) + SET(sc->flags, SHF_USE_DMA); +#endif + + /* + * Determine the base clock frequency. (2.2.24) + */ + + sc->clkbase = 96 * 1000; +#if 0 + if (SDHC_BASE_FREQ_KHZ(caps) != 0) + sc->clkbase = SDHC_BASE_FREQ_KHZ(caps); + sc->clkbase = SDHC_BASE_FREQ_KHZ(caps); +#endif + if (sc->clkbase == 0) { + /* The attachment driver must tell us. */ + printf("%s: base clock frequency unknown\n", DEVNAME(sc)); + goto err; + } else if (sc->clkbase < 10000 || sc->clkbase > 96000) { + /* SDHC 1.0 supports only 10-63 MHz. */ + printf("%s: base clock frequency out of range: %u MHz\n", + DEVNAME(sc), sc->clkbase / 1000); + goto err; + } + + /* + * XXX Set the data timeout counter value according to + * capabilities. (2.2.15) + */ + + + /* + * Determine SD bus voltage levels supported by the controller. + */ + if (caps & MMCHS_CAPA_VS18) + SET(sc->ocr, MMC_OCR_1_65V_1_95V); + if (caps & MMCHS_CAPA_VS30) + SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); + if (caps & MMCHS_CAPA_VS33) + SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); + + /* + * Omap max block size is fixed (single buffer), could limit + * this to 512 for double buffering, but dont see the point. + */ + switch ((caps & MMCHS_CAPA_MBL_MASK) >> MMCHS_CAPA_MBL_SHIFT) { + case 0: + sc->maxblklen = 512; + break; + case 1: + sc->maxblklen = 1024; + break; + case 2: + sc->maxblklen = 2048; + break; + default: + sc->maxblklen = 512; + printf("invalid capability blocksize in capa %08x," + " trying 512\n", HREAD4(sc, MMCHS_CAPA)); + } + /* + * MMC does not support blksize > 512 yet + */ + sc->maxblklen = 512; + /* + * Attach the generic SD/MMC bus driver. (The bus driver must + * not invoke any chipset functions before it is attached.) + */ + bzero(&saa, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &ommmc_functions; + saa.sch = sc; + saa.caps = SMC_CAPS_4BIT_MODE; + if (caps & MMCHS_CAPA_HSS) + saa.caps |= SMC_CAPS_MMC_HIGHSPEED; + + sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL); + if (sc->sdmmc == NULL) { + printf("%s: can't attach sdmmc\n", DEVNAME(sc)); + goto err; + } + + return; +err: + if (sc->sc_ih != NULL) + arm_intr_disestablish(sc->sc_ih); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, aa->aa_dev->mem[0].size); +} + + +/* + * Power hook established by or called from attachment driver. + */ +void +ommmc_power(int why, void *arg) +{ +#if 0 + struct ommmc_softc *sc = arg; + int n, i; +#endif + + switch(why) { + case DVACT_SUSPEND: + /* XXX poll for command completion or suspend command + * in progress */ + + /* Save the host controller state. */ +#if 0 + for (i = 0; i < sizeof sc->regs; i++) + sc->regs[i] = HREAD1(sc, i); +#endif + break; + + case DVACT_RESUME: + /* Restore the host controller state. */ +#if 0 + (void)ommmc_host_reset(sc); + for (i = 0; i < sizeof sc->regs; i++) + HWRITE1(sc, i, sc->regs[i]); +#endif + break; + } +} + +/* + * Shutdown hook established by or called from attachment driver. + */ +void +ommmc_shutdown(void *arg) +{ + struct ommmc_softc *sc = arg; + + /* XXX chip locks up if we don't disable it before reboot. */ + (void)ommmc_host_reset(sc); +} + +/* + * Reset the host controller. Called during initialization, when + * cards are removed, upon resume, and during error recovery. + */ +int +ommmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct ommmc_softc *sc = sch; + uint32_t imask; + int error; + int s; + + s = splsdmmc(); + + /* Disable all interrupts. */ + HWRITE4(sc, MMCHS_IE, 0); + HWRITE4(sc, MMCHS_ISE, 0); + + /* + * Reset the entire host controller and wait up to 100ms for + * the controller to clear the reset bit. + */ + if ((error = ommmc_soft_reset(sc, MMCHS_SYSCTL_SRA)) != 0) { + splx(s); + return (error); + } + +#if 0 + HSET4(sc, MMCHS_CON, MMCHS_CON_INIT); + HWRITE4(sc, MMCHS_CMD, 0); + delay(100); /* should delay 1ms */ + + HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CC); + HCLR4(sc, MMCHS_CON, MMCHS_CON_INIT); + HWRITE4(sc, MMCHS_STAT, ~0); +#endif + + + /* Set data timeout counter value to max for now. */ + HSET4(sc, MMCHS_SYSCTL, 0xe << MMCHS_SYSCTL_DTO_SH); + + /* Enable interrupts. */ + imask = MMCHS_STAT_BRR | MMCHS_STAT_BWR | MMCHS_STAT_BGE | + MMCHS_STAT_TC | MMCHS_STAT_CC; + + imask |= MMCHS_STAT_BADA | MMCHS_STAT_CERR | MMCHS_STAT_DEB | + MMCHS_STAT_DCRC | MMCHS_STAT_DTO | MMCHS_STAT_CIE | + MMCHS_STAT_CEB | MMCHS_STAT_CCRC | MMCHS_STAT_CTO; + + HWRITE4(sc, MMCHS_IE, imask); + HWRITE4(sc, MMCHS_ISE, imask); + + splx(s); + return (0); +} + +uint32_t +ommmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + struct ommmc_softc *sc = sch; + return (sc->ocr); +} + +int +ommmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + struct ommmc_softc *sc = sch; + return (sc->maxblklen); +} + +/* + * Return non-zero if the card is currently inserted. + */ +int +ommmc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct ommmc_softc *sc = sch; + return !ISSET(HREAD4(sc, MMCHS_SYSTEST), MMCHS_SYSTEST_SDCD) ? + 1 : 0; +} + +/* + * Set or change SD bus voltage and enable or disable SD bus power. + * Return zero on success. + */ +int +ommmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + struct ommmc_softc *sc = sch; + uint32_t vdd; + uint32_t reg; + int s; + + s = splsdmmc(); + + /* + * Disable bus power before voltage change. + */ + HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP); + + /* If power is disabled, reset the host and return now. */ + if (ocr == 0) { + splx(s); + (void)ommmc_host_reset(sc); + return (0); + } + + /* + * Select the maximum voltage according to capabilities. + */ + ocr &= sc->ocr; + + if (ISSET(ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V)) + vdd = MMCHS_HCTL_SDVS_V33; + else if (ISSET(ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V)) + vdd = MMCHS_HCTL_SDVS_V30; + else if (ISSET(ocr, MMC_OCR_1_65V_1_95V)) + vdd = MMCHS_HCTL_SDVS_V18; + else { + /* Unsupported voltage level requested. */ + splx(s); + return (EINVAL); + } + + /* + * Enable bus power. Wait at least 1 ms (or 74 clocks) plus + * voltage ramp until power rises. + */ + reg = HREAD4(sc, MMCHS_HCTL); + reg &= ~MMCHS_HCTL_SDVS_MASK; + reg |= vdd; + HWRITE4(sc, MMCHS_HCTL, reg); + + HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP); + delay(10000); /* XXX */ + + /* + * The host system may not power the bus due to battery low, + * etc. In that case, the host controller should clear the + * bus power bit. + */ + if (!ISSET(HREAD4(sc, MMCHS_HCTL), MMCHS_HCTL_SDBP)) { + splx(s); + return (ENXIO); + } + + splx(s); + return (0); +} + +/* + * Return the smallest possible base clock frequency divisor value + * for the CLOCK_CTL register to produce `freq' (KHz). + */ +static int +ommmc_clock_divisor(struct ommmc_softc *sc, uint32_t freq) +{ + int div; + uint32_t maxclk = MMCHS_SYSCTL_CLKD_MASK>>MMCHS_SYSCTL_CLKD_SH; + + for (div = 1; div <= maxclk; div++) + if ((sc->clkbase / div) <= freq) { + return (div); + } + + printf("divisor failure\n"); + /* No divisor found. */ + return (-1); +} + +/* + * Set or change SDCLK frequency or disable the SD clock. + * Return zero on success. + */ +int +ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + int error = 0; + struct ommmc_softc *sc = sch; + uint32_t reg; + int s; + int div; + int timo; + + s = splsdmmc(); + + /* Must not stop the clock if commands are in progress. */ + for (timo = 1000; timo > 0; timo--) { + if (!ISSET(HREAD4(sc, MMCHS_PSTATE), + MMCHS_PSTATE_CMDI|MMCHS_PSTATE_DATI)) + break; + delay(10); + } + if (timo == 0) { + error = ETIMEDOUT; + goto ret; + } + + /* + * Stop SD clock before changing the frequency. + */ + HCLR4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN); + if (freq == SDMMC_SDCLK_OFF) + goto ret; + + /* + * Set the minimum base clock frequency divisor. + */ + if ((div = ommmc_clock_divisor(sc, freq)) < 0) { + /* Invalid base clock frequency or `freq' value. */ + error = EINVAL; + goto ret; + } + reg = HREAD4(sc, MMCHS_SYSCTL); + reg &= ~MMCHS_SYSCTL_CLKD_MASK; + reg |= div << MMCHS_SYSCTL_CLKD_SH; + HWRITE4(sc, MMCHS_SYSCTL, reg); + + /* + * Start internal clock. Wait 10ms for stabilization. + */ + HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_ICE); + for (timo = 1000; timo > 0; timo--) { + if (ISSET(HREAD4(sc, MMCHS_SYSCTL), MMCHS_SYSCTL_ICS)) + break; + delay(10); + } + if (timo == 0) { + error = ETIMEDOUT; + goto ret; + } + + /* + * Enable SD clock. + */ + HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN); +ret: + splx(s); + return (error); +} + +int +ommmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct ommmc_softc *sc = sch; + int s; + + if (width != 1 && width != 4 && width != 8) + return (1); + + s = splsdmmc(); + + if (width == 8) + HSET4(sc, MMCHS_CON, MMCHS_CON_DW8); + else + HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8); + + if (width == 4) + HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); + else if (width == 1) + HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); + + splx(s); + + return (0); +} + +void +ommmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ + /* - this is SDIO card interrupt */ + struct ommmc_softc *sc = sch; + + if (enable) { + HSET4(sc, MMCHS_IE, MMCHS_STAT_CIRQ); + HSET4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ); + } else { + HCLR4(sc, MMCHS_IE, MMCHS_STAT_CIRQ); + HCLR4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ); + } +} + +void +ommmc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + struct ommmc_softc *sc = sch; + + HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ); +} + +int +ommmc_wait_state(struct ommmc_softc *sc, uint32_t mask, uint32_t value) +{ + uint32_t state; + int timeout; + + state = HREAD4(sc, MMCHS_PSTATE); + DPRINTF(3,("%s: wait_state %x %x %x(state=%b)\n", DEVNAME(sc), + mask, value, state, state, MMCHS_PSTATE_FMT)); + for (timeout = 1000; timeout > 0; timeout--) { + if (((state = HREAD4(sc, MMCHS_PSTATE)) & mask) == value) + return (0); + delay(10); + } + DPRINTF(0,("%s: timeout waiting for %x (state=%b)\n", DEVNAME(sc), + value, state, MMCHS_PSTATE_FMT)); + return (ETIMEDOUT); +} + +void +ommmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct ommmc_softc *sc = sch; + int error; + + /* + * Start the MMC command, or mark `cmd' as failed and return. + */ + error = ommmc_start_command(sc, cmd); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * Wait until the command phase is done, or until the command + * is marked done for any other reason. + */ + if (!ommmc_wait_intr(sc, MMCHS_STAT_CC, SDHC_COMMAND_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * The host controller removes bits [0:7] from the response + * data (CRC) and we pass the data up unchanged to the bus + * driver (without padding). + */ + if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + uint32_t v0,v1,v2,v3; + v0 = HREAD4(sc, MMCHS_RSP10); + v1 = HREAD4(sc, MMCHS_RSP32); + v2 = HREAD4(sc, MMCHS_RSP54); + v3 = HREAD4(sc, MMCHS_RSP76); + + cmd->c_resp[0] = (v0 >> 8) | ((v1 & 0xff) << 24); + cmd->c_resp[1] = (v1 >> 8) | ((v2 & 0xff) << 24); + cmd->c_resp[2] = (v2 >> 8) | ((v3 & 0xff) << 24); + cmd->c_resp[3] = v3 >> 8; +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]); +#endif + } else { + cmd->c_resp[0] = HREAD4(sc, MMCHS_RSP10); +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\n", cmd->c_resp[0]); +#endif + } + } + + /* + * If the command has data to transfer in any direction, + * execute the transfer now. + */ + if (cmd->c_error == 0 && cmd->c_data != NULL) + ommmc_transfer_data(sc, cmd); + +#if 0 + /* Turn off the LED. */ + HCLR1(sc, SDHC_HOST_CTL, SDHC_LED_ON); +#endif + + DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n", + DEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error)); + SET(cmd->c_flags, SCF_ITSDONE); +} + +int +ommmc_start_command(struct ommmc_softc *sc, struct sdmmc_command *cmd) +{ + uint32_t blksize = 0; + uint32_t blkcount = 0; + uint32_t command; + int error; + int s; + + DPRINTF(1,("%s: start cmd %u arg=%#x data=%p dlen=%d flags=%#x " + "proc=\"%s\"\n", DEVNAME(sc), cmd->c_opcode, cmd->c_arg, + cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ? + curproc->p_comm : "")); + + /* + * The maximum block length for commands should be the minimum + * of the host buffer size and the card buffer size. (1.7.2) + */ + + /* Fragment the data into proper blocks. */ + if (cmd->c_datalen > 0) { + blksize = MIN(cmd->c_datalen, cmd->c_blklen); + blkcount = cmd->c_datalen / blksize; + if (cmd->c_datalen % blksize > 0) { + /* XXX: Split this command. (1.7.4) */ + printf("%s: data not a multiple of %d bytes\n", + DEVNAME(sc), blksize); + return (EINVAL); + } + } + + /* Check limit imposed by 9-bit block count. (1.7.2) */ + if (blkcount > MMCHS_BLK_NBLK_MAX) { + printf("%s: too much data\n", DEVNAME(sc)); + return (EINVAL); + } + + /* Prepare transfer mode register value. (2.2.5) */ + command = 0; + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + command |= MMCHS_CMD_DDIR; + if (blkcount > 0) { + command |= MMCHS_CMD_BCE; + if (blkcount > 1) { + command |= MMCHS_CMD_MSBS; + /* XXX only for memory commands? */ + command |= MMCHS_CMD_ACEN; + } + } +#ifdef notyet + if (ISSET(sc->flags, SHF_USE_DMA)) + command |= MMCHS_CMD_DE; +#endif + + /* + * Prepare command register value. (2.2.6) + */ + command |= (cmd->c_opcode << MMCHS_CMD_INDX_SHIFT) & + MMCHS_CMD_INDX_SHIFT_MASK; + + if (ISSET(cmd->c_flags, SCF_RSP_CRC)) + command |= MMCHS_CMD_CCCE; + if (ISSET(cmd->c_flags, SCF_RSP_IDX)) + command |= MMCHS_CMD_CICE; + if (cmd->c_data != NULL) + command |= MMCHS_CMD_DP; + + if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + command |= MMCHS_CMD_RESP_NONE; + else if (ISSET(cmd->c_flags, SCF_RSP_136)) + command |= MMCHS_CMD_RESP136; + else if (ISSET(cmd->c_flags, SCF_RSP_BSY)) + command |= MMCHS_CMD_RESP48B; + else + command |= MMCHS_CMD_RESP48; + + /* Wait until command and data inhibit bits are clear. (1.5) */ + if ((error = ommmc_wait_state(sc, MMCHS_PSTATE_CMDI, 0)) != 0) + return (error); + + s = splsdmmc(); + +#if 0 + /* Alert the user not to remove the card. */ + HSET1(sc, SDHC_HOST_CTL, SDHC_LED_ON); +#endif + + /* XXX: Set DMA start address if SHF_USE_DMA is set. */ + + DPRINTF(1,("%s: cmd=%#x blksize=%d blkcount=%d\n", + DEVNAME(sc), command, blksize, blkcount)); + + /* + * Start a CPU data transfer. Writing to the high order byte + * of the SDHC_COMMAND register triggers the SD command. (1.5) + */ + HWRITE4(sc, MMCHS_BLK, (blkcount << MMCHS_BLK_NBLK_SHIFT) | + (blksize << MMCHS_BLK_BLEN_SHIFT)); + HWRITE4(sc, MMCHS_ARG, cmd->c_arg); + HWRITE4(sc, MMCHS_CMD, command); + + splx(s); + return (0); +} + +void +ommmc_transfer_data(struct ommmc_softc *sc, struct sdmmc_command *cmd) +{ + uint8_t *datap = cmd->c_data; + int i, datalen; + int mask; + int error; + + mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? + MMCHS_PSTATE_BRE : MMCHS_PSTATE_BWE; + error = 0; + datalen = cmd->c_datalen; + + DPRINTF(1,("%s: resp=%#x datalen=%d\n", DEVNAME(sc), + MMC_R1(cmd->c_resp), datalen)); + + while (datalen > 0) { + if (!ommmc_wait_intr(sc, MMCHS_STAT_BRR| MMCHS_STAT_BWR, + SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; + break; + } + + if ((error = ommmc_wait_state(sc, mask, mask)) != 0) + break; + + i = MIN(datalen, cmd->c_blklen); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + ommmc_read_data(sc, datap, i); + else + ommmc_write_data(sc, datap, i); + + datap += i; + datalen -= i; + } + + if (error == 0 && !ommmc_wait_intr(sc, MMCHS_STAT_TC, + SDHC_TRANSFER_TIMEOUT)) + error = ETIMEDOUT; + + if (error != 0) + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + + DPRINTF(1,("%s: data transfer done (error=%d)\n", + DEVNAME(sc), cmd->c_error)); +} + +void +ommmc_read_data(struct ommmc_softc *sc, uint8_t *datap, int datalen) +{ + while (datalen > 3) { + *(uint32_t *)datap = HREAD4(sc, MMCHS_DATA); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = HREAD4(sc, MMCHS_DATA); + do { + *datap++ = rv & 0xff; + rv = rv >> 8; + } while (--datalen > 0); + } +} + +void +ommmc_write_data(struct ommmc_softc *sc, uint8_t *datap, int datalen) +{ + while (datalen > 3) { + DPRINTF(3,("%08x\n", *(uint32_t *)datap)); + HWRITE4(sc, MMCHS_DATA, *((uint32_t *)datap)); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = *datap++; + if (datalen > 1) + rv |= *datap++ << 8; + if (datalen > 2) + rv |= *datap++ << 16; + DPRINTF(3,("rv %08x\n", rv)); + HWRITE4(sc, MMCHS_DATA, rv); + } +} + +/* Prepare for another command. */ +int +ommmc_soft_reset(struct ommmc_softc *sc, int mask) +{ + + int timo; + + DPRINTF(1,("%s: software reset reg=%#x\n", DEVNAME(sc), mask)); + + HSET4(sc, MMCHS_SYSCTL, mask); + delay(10); + for (timo = 1000; timo > 0; timo--) { + if (!ISSET(HREAD4(sc, MMCHS_SYSCTL), mask)) + break; + delay(10); + } + if (timo == 0) { + DPRINTF(1,("%s: timeout reg=%#x\n", DEVNAME(sc), + HREAD4(sc, MMCHS_SYSCTL))); + return (ETIMEDOUT); + } + + return (0); +} + +int +ommmc_wait_intr(struct ommmc_softc *sc, int mask, int timo) +{ + int status; + int s; + + mask |= MMCHS_STAT_ERRI; + + s = splsdmmc(); + status = sc->intr_status & mask; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + status |= MMCHS_STAT_ERRI; + break; + } + status = sc->intr_status & mask; + } + sc->intr_status &= ~status; + + DPRINTF(2,("%s: intr status %#x error %#x\n", DEVNAME(sc), status, + sc->intr_error_status)); + + /* Command timeout has higher priority than command complete. */ + if (ISSET(status, MMCHS_STAT_ERRI)) { + sc->intr_error_status = 0; + (void)ommmc_soft_reset(sc, MMCHS_SYSCTL_SRC|MMCHS_SYSCTL_SRD); + status = 0; + } + + splx(s); + return (status); +} + +/* + * Established by attachment driver at interrupt priority IPL_SDMMC. + */ +int +ommmc_intr(void *arg) +{ + struct ommmc_softc *sc = arg; + + uint32_t status; + + /* Find out which interrupts are pending. */ + status = HREAD4(sc, MMCHS_STAT); + + /* Acknowledge the interrupts we are about to handle. */ + HWRITE4(sc, MMCHS_STAT, status); + DPRINTF(2,("%s: interrupt status=%b\n", DEVNAME(sc), + status, MMCHS_STAT_FMT)); + + /* + * Service error interrupts. + */ + if (ISSET(status, MMCHS_STAT_ERRI)) { + if (ISSET(status, MMCHS_STAT_CTO| + MMCHS_STAT_DTO)) { + sc->intr_status |= status; + sc->intr_error_status |= status & 0xffff0000; + wakeup(&sc->intr_status); + } + } + +#if 0 + /* + * Wake up the sdmmc event thread to scan for cards. + */ + if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) + ommmc_needs_discover(sc->sdmmc); +#endif + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, MMCHS_STAT_BRR| + MMCHS_STAT_BWR|MMCHS_STAT_TC| + MMCHS_STAT_CC)) { + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + /* + * Service SD card interrupts. + */ + if (ISSET(status, MMCHS_STAT_CIRQ)) { + DPRINTF(0,("%s: card interrupt\n", DEVNAME(sc))); + HCLR4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ); + sdmmc_card_intr(sc->sdmmc); + } + return 1; +} + +#ifdef SDHC_DEBUG +void +ommmc_dump_regs(struct ommmc_softc *sc) +{ +} +#endif diff --git a/omohci.c b/omohci.c new file mode 100644 index 0000000..b4e1a8a --- /dev/null +++ b/omohci.c @@ -0,0 +1,351 @@ +/* $OpenBSD: omohci.c,v 1.3 2014/05/19 13:11:31 mpi Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define HOSTUEADDR 0x0E0 +#define HOSTUESTATUS 0x0E4 +#define HOSTTIMEOUTCTRL 0x0E8 +#define HOSTREVISION 0x0EC +#define WHM_REVID_REGISTER 0x0F4 +#define WHM_TEST_OBSV 0x0F8 +#define WHM_TEST_CTL 0x0FC +#define HHC_TEST_CFG 0x100 +#define HHC_TEST_CTL 0x104 +#define HHC_TEST_OBSV 0x108 +#define REVDEV 0x200 /* 16 bit */ +#define EP_NUM 0x204 /* 16 bit */ +#define DATA 0x208 /* 16 bit */ +#define CTRL 0x20C /* 16 bit */ +#define STAT_FLG 0x210 /* 16 bit */ +#define RXFSTAT 0x214 /* 16 bit */ +#define SYSCON1 0x218 /* 16 bit */ +#define SYSCON2 0x21C /* 16 bit */ +#define DEVSTAT 0x220 /* 16 bit */ +#define SOFREG 0x224 /* 16 bit */ +#define IRQ_EN 0x228 /* 16 bit */ +#define DMA_IRQ_EN 0x22C /* 16 bit */ +#define IRQ_SRC 0x230 /* 16 bit */ +#define EPN_STAT 0x234 /* 16 bit */ +#define DMAN_STAT 0x238 /* 16 bit */ +#define RXDMA_CFG 0x240 /* 16 bit */ +#define TXDMA_CFG 0x244 /* 16 bit */ + +#define TXDMA0 0x250 +#define TXDMA1 0x254 +#define TXDMA2 0x258 +#define RXDMA0 0x260 +#define RXDMA1 0x264 +#define RXDMA2 0x268 + +#define EP0 0x280 +#define EP_RX(x) 0x280 + (x * 4) +#define EP_TX(x) 0x2C0 + (x * 4) + +#define OTG_REV 0x300 +#define OTG_SYSCON_1 0x304 +#define OTG_SYSCON_2 0x308 +#define OTG_SYSCON2_OTG_EN 0x80000000 +#define OTG_SYSCON2_UHOST_EN 0x00000100 +#define OTG_SYSCON2_MODE_DISABLED 0x00000000 +#define OTG_SYSCON2_MODE_CLIENT 0x00000001 +#define OTG_SYSCON2_MODE_HOST 0x00000004 +#define OTG_CTRL 0x30C +#if 0 +#define OTG_IRQ_EN 0x310 /* 16 bit */ +#define OTG_IRQ_SRC 0x314 /* 16 bit */ +#define OTG_OUTCTRL 0x318 /* 16 bit */ +#define OTG_TEST 0x320 /* 16 bit */ +#endif +#define OTG_VC 0x3FC + + +int omohci_match(struct device *, void *, void *); +void omohci_attach(struct device *, struct device *, void *); +int omohci_detach(struct device *, int); +int omohci_activate(struct device *, int); + +struct omohci_softc { + struct ohci_softc sc; + void *sc_ihc0; + void *sc_ihc1; + void *sc_ihc2; + void *sc_ih0; + void *sc_ih1; + void *sc_ihotg; +}; + +void omohci_enable(struct omohci_softc *); +void omohci_disable(struct omohci_softc *); + +struct cfattach omohci_ca = { + sizeof (struct omohci_softc), omohci_match, omohci_attach, + omohci_detach, omohci_detach +}; + +int +omohci_match(struct device *parent, void *match, void *aux) +{ +#if 0 + if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) != CPU_ID_PXA27X) + return (0); +#endif + + return (1); +} + +void +omohci_attach(struct device *parent, struct device *self, void *aux) +{ + struct omohci_softc *sc = (struct omohci_softc *)self; + struct ahb_attach_args *aa = aux; + usbd_status r; + + sc->sc.iot = aa->aa_iot; + sc->sc.sc_bus.dmatag = aa->aa_dmat; + sc->sc_ih0 = NULL; + sc->sc_ih1 = NULL; + sc->sc_ihc0 = NULL; + sc->sc_ihc1 = NULL; + sc->sc_ihc2 = NULL; + sc->sc_ihotg = NULL; + sc->sc.sc_size = 0; + + /* Map I/O space */ + if (bus_space_map(sc->sc.iot, aa->aa_addr, aa->aa_size, 0, + &sc->sc.ioh)) { + printf(": cannot map mem space\n"); + return; + } + sc->sc.sc_size = aa->aa_size; + + /* XXX copied from ohci_pci.c. needed? */ + bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size, + BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); + +#if 0 + /* start the usb clock */ + pxa2x0_clkman_config(CKEN_USBHC, 1); +#endif + omohci_enable(sc); + + /* Disable interrupts, so we don't get any spurious ones. */ + bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE, + OHCI_MIE); + + sc->sc_ihc0 = arm_intr_establish(aa->aa_intr, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + sc->sc_ihc1 = arm_intr_establish(aa->aa_intr+1, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + sc->sc_ihc2 = arm_intr_establish(aa->aa_intr+2, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + sc->sc_ih0 = arm_intr_establish(aa->aa_intr+3, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + sc->sc_ih1 = arm_intr_establish(aa->aa_intr+4, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + sc->sc_ihotg = arm_intr_establish(aa->aa_intr+5, IPL_USB, + ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); + if (sc->sc_ih0 == NULL || + sc->sc_ih1 == NULL || + sc->sc_ihc0 == NULL || + sc->sc_ihc1 == NULL || + sc->sc_ihc2 == NULL || + sc->sc_ihotg == NULL) { + printf(": unable to establish interrupt\n"); + omohci_disable(sc); +#if 0 + pxa2x0_clkman_config(CKEN_USBHC, 0); +#endif + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; + return; + } + + prcm_enablemodule(PRCM_USB); + + bus_space_write_4(sc->sc.iot, sc->sc.ioh, OTG_SYSCON_2, + OTG_SYSCON2_UHOST_EN | OTG_SYSCON2_MODE_HOST); + + strlcpy(sc->sc.sc_vendor, "OMAP24xx", sizeof(sc->sc.sc_vendor)); + r = ohci_init(&sc->sc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: init failed, error=%d\n", + sc->sc.sc_bus.bdev.dv_xname, r); + arm_intr_disestablish(sc->sc_ih0); + arm_intr_disestablish(sc->sc_ih1); + arm_intr_disestablish(sc->sc_ihc0); + arm_intr_disestablish(sc->sc_ihc1); + arm_intr_disestablish(sc->sc_ihc2); + arm_intr_disestablish(sc->sc_ihotg); + sc->sc_ih0 = NULL; + sc->sc_ih1 = NULL; + sc->sc_ihc0 = NULL; + sc->sc_ihc1 = NULL; + sc->sc_ihc2 = NULL; + sc->sc_ihotg = NULL; + omohci_disable(sc); +#if 0 + pxa2x0_clkman_config(CKEN_USBHC, 0); +#endif + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; + return; + } + + config_found(self, &sc->sc.sc_bus, usbctlprint); +} + +int +omohci_detach(struct device *self, int flags) +{ + struct omohci_softc *sc = (struct omohci_softc *)self; + int rv; + + rv = ohci_detach(self, flags); + if (rv) + return (rv); + + if (sc->sc_ih0 != NULL) { + arm_intr_disestablish(sc->sc_ih0); + arm_intr_disestablish(sc->sc_ih1); + arm_intr_disestablish(sc->sc_ihc0); + arm_intr_disestablish(sc->sc_ihc1); + arm_intr_disestablish(sc->sc_ihc2); + arm_intr_disestablish(sc->sc_ihotg); + sc->sc_ih0 = NULL; + sc->sc_ih1 = NULL; + sc->sc_ihc0 = NULL; + sc->sc_ihc1 = NULL; + sc->sc_ihc2 = NULL; + sc->sc_ihotg = NULL; + } + + omohci_disable(sc); + + /* stop clock */ +#if 0 + pxa2x0_clkman_config(CKEN_USBHC, 0); +#endif + + if (sc->sc.sc_size) { + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; + } + + return (0); +} + + +int +omohci_activate(struct device *self, int act) +{ + struct omohci_softc *sc = (struct omohci_softc *)self; + + switch (act) { + case DVACT_SUSPEND: + sc->sc.sc_bus.use_polling++; + ohci_power(why, &sc->sc); +#if 0 + pxa2x0_clkman_config(CKEN_USBHC, 0); +#endif + sc->sc.sc_bus.use_polling--; + break; + + case DVACT_RESUME: + sc->sc.sc_bus.use_polling++; +#if 0 + pxa2x0_clkman_config(CKEN_USBHC, 1); +#endif + omohci_enable(sc); + ohci_power(why, &sc->sc); + sc->sc.sc_bus.use_polling--; + break; + } + return 0; +} + +void +omohci_enable(struct omohci_softc *sc) +{ +#if 0 + u_int32_t hr; + + /* Full host reset */ + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) | USBHC_HR_FHR); + + DELAY(USBHC_RST_WAIT); + + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR)); + + /* Force system bus interface reset */ + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) | USBHC_HR_FSBIR); + + while (bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR) & \ + USBHC_HR_FSBIR) + DELAY(3); + + /* Enable the ports (physically only one, only enable that one?) */ + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSE)); + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSEP2)); +#endif +} + +void +omohci_disable(struct omohci_softc *sc) +{ +#if 0 + u_int32_t hr; + + /* Full host reset */ + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) | USBHC_HR_FHR); + + DELAY(USBHC_RST_WAIT); + + hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); + bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, + (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR)); +#endif +} diff --git a/omusbtll.c b/omusbtll.c new file mode 100644 index 0000000..3ab7553 --- /dev/null +++ b/omusbtll.c @@ -0,0 +1,175 @@ +/* $OpenBSD: omusbtll.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */ +/* + * Copyright (c) 2010 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define USBTLL_REVISION 0x0000 +#define USBTLL_SYSCONFIG 0x0010 +#define USBTLL_SYSSTATUS 0x0014 +#define USBTLL_IRQSTATUS 0x0018 +#define USBTLL_IRQENABLE 0x001C +#define USBTLL_SHARED_CONF 0x0030 +#define USBTLL_SHARED_CONF_USB_90D_DDR_EN (1<<6) +#define USBTLL_SHARED_CONF_USB_180D_SDR_EN (1<<5) +#define USBTLL_SHARED_CONF_USB_DIVRATIO_SH 2 +#define USBTLL_SHARED_CONF_FCLK_REQ (1<<1) +#define USBTLL_SHARED_CONF_FCLK_IS_ON (1<<0) + +#define USBTLL_CHANNEL_CONF_(i) (0x0040 + (0x04 * (i))) +#define USBTLL_CHANNEL_CONF_FSLSLINESTATE_SH 28 +#define USBTLL_CHANNEL_CONF_FSLSMODE_SH 24 +#define USBTLL_CHANNEL_CONF_TESTTXSE0 (1<<20) +#define USBTLL_CHANNEL_CONF_TESTTXDAT (1<<19) +#define USBTLL_CHANNEL_CONF_TESTTXEN (1<<18) +#define USBTLL_CHANNEL_CONF_TESTEN (1<<17) +#define USBTLL_CHANNEL_CONF_DRVVBUS (1<<16) +#define USBTLL_CHANNEL_CONF_CHRGVBUS (1<<15) +#define USBTLL_CHANNEL_CONF_ULPINOBITSTUFF (1<<11) +#define USBTLL_CHANNEL_CONF_ULPIAUTOIDLE (1<<10) +#define USBTLL_CHANNEL_CONF_UTMIAUTOIDLE (1<<9) +#define USBTLL_CHANNEL_CONF_ULPIDDRMODE (1<<8) +#define USBTLL_CHANNEL_CONF_LPIOUTCLKMODE (1<<7) +#define USBTLL_CHANNEL_CONF_TLLFULLSPEED (1<<6) +#define USBTLL_CHANNEL_CONF_TLLCONNECT (1<<5) +#define USBTLL_CHANNEL_CONF_TLLATTACH (1<<4) +#define USBTLL_CHANNEL_CONF_UTMIISADEV (1<<3) +#define USBTLL_CHANNEL_CONF_CHANMODE_SH 1 +#define USBTLL_CHANNEL_CONF_CHANEN (1<<0) + +/* +ULPI_VENDOR_ID_LO_(i) (0x0800 + (0x100 * (i))) +ULPI_VENDOR_ID_HI_(i) (0x0801 + (0x100 * (i))) +ULPI_PRODUCT_ID_LO_(i) (0x0802 + (0x100 * (i))) +ULPI_PRODUCT_ID_HI_(i) (0x0803 + (0x100 * (i))) +ULPI_FUNCTION_CTRL_(i) (0x0804 + (0x100 * (i))) +ULPI_FUNCTION_CTRL_SET_(i) (0x0805 + (0x100 * (i))) +ULPI_FUNCTION_CTRL_CLR_(i) (0x0806 + (0x100 * (i))) +ULPI_INTERFACE_CTRL_(i) (0x0807 + (0x100 * (i))) +ULPI_INTERFACE_CTRL_SET_(i) (0x0808 + (0x100 * (i))) +ULPI_INTERFACE_CTRL_CLR_(i) (0x0809 + (0x100 * (i))) +ULPI_OTG_CTRL_(i) (0x080A + (0x100 * (i))) +ULPI_OTG_CTRL_SET_(i) (0x080B + (0x100 * (i))) +ULPI_OTG_CTRL_CLR_(i) (0x080C + (0x100 * (i))) +ULPI_USB_INT_EN_RISE_(i) (0x080D + (0x100 * (i))) +ULPI_USB_INT_EN_RISE_SET_(i) (0x080E + (0x100 * (i))) +ULPI_USB_INT_EN_RISE_CLR_(i) (0x080F + (0x100 * (i))) +ULPI_USB_INT_EN_FALL_(i) (0x0810 + (0x100 * (i))) +ULPI_USB_INT_EN_FALL_SET_(i) (0x0811 + (0x100 * (i))) +ULPI_USB_INT_EN_FALL_CLR_(i) (0x0812 + (0x100 * (i))) +ULPI_USB_INT_STATUS_(i) (0x0813 + (0x100 * (i))) +ULPI_USB_INT_LATCH_(i) (0x0814 + (0x100 * (i))) +*/ + +struct omusbtll_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +void omusbtll_attach(struct device *parent, struct device *self, void *args); +void omusbtll_init(uint32_t channel_mask); + +struct cfattach omusbtll_ca = { + sizeof (struct omusbtll_softc), NULL, omusbtll_attach +}; + +struct cfdriver omusbtll_cd = { + NULL, "omusbtll", DV_DULL +}; + +struct omusbtll_softc *omusbtll_sc; +void +omusbtll_attach(struct device *parent, struct device *self, void *args) +{ + struct omusbtll_softc *sc = (struct omusbtll_softc *) self; + struct armv7_attach_args *aa = args; + u_int32_t rev; + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) { + printf("%s: bus_space_map failed!\n", __func__); + return; + } + +#if 0 + prcm_enablemodule(PRCM_USBHOST1); + prcm_enablemodule(PRCM_USBHOST2); +#endif + prcm_enablemodule(PRCM_USBTLL); + + delay(10000); + + //return; +#if 1 + rev = bus_space_read_1(sc->sc_iot, sc->sc_ioh, USBTLL_SYSCONFIG); + + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); +#endif + + omusbtll_sc = sc; + + omusbtll_init(0x3); +} + + +void omusbtll_init(uint32_t channel_mask) +{ + int i; + uint32_t val; + /* global reacharound */ + struct omusbtll_softc *sc = omusbtll_sc; + + for(i = 0; i < 3; i++) { + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + USBTLL_CHANNEL_CONF_(i)); + val &= ~(USBTLL_CHANNEL_CONF_ULPINOBITSTUFF | + USBTLL_CHANNEL_CONF_ULPIAUTOIDLE | + USBTLL_CHANNEL_CONF_ULPIDDRMODE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + USBTLL_CHANNEL_CONF_(i), val); + } + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBTLL_SHARED_CONF); + val |= (USBTLL_SHARED_CONF_USB_180D_SDR_EN | + (1 << USBTLL_SHARED_CONF_USB_DIVRATIO_SH) | + USBTLL_SHARED_CONF_FCLK_IS_ON); + val &= ~(USBTLL_SHARED_CONF_USB_90D_DDR_EN); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBTLL_SHARED_CONF, val); + + for (i = 0; i < 3; i++) { + if (channel_mask & (1<sc_iot, sc->sc_ioh, + USBTLL_CHANNEL_CONF_(i)); + + val |= USBTLL_CHANNEL_CONF_CHANEN; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + USBTLL_CHANNEL_CONF_(i), val); + printf("usbtll enabling %d\n", i); + } + } +} diff --git a/prcm.c b/prcm.c new file mode 100644 index 0000000..52b39c4 --- /dev/null +++ b/prcm.c @@ -0,0 +1,542 @@ +/* $OpenBSD: prcm.c,v 1.9 2014/05/08 21:17:01 miod Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * 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. + * 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. + */ + +/* + * Driver for the Power, Reset and Clock Management Module (PRCM). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PRCM_REVISION 0x0800 +#define PRCM_SYSCONFIG 0x0810 + +uint32_t prcm_imask_mask[PRCM_REG_MAX]; +uint32_t prcm_fmask_mask[PRCM_REG_MAX]; +uint32_t prcm_imask_addr[PRCM_REG_MAX]; +uint32_t prcm_fmask_addr[PRCM_REG_MAX]; + +#define SYS_CLK 13 /* SYS_CLK speed in MHz */ + +struct prcm_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_prcm; + bus_space_handle_t sc_cm1; + bus_space_handle_t sc_cm2; + void (*sc_setup)(struct prcm_softc *sc); + void (*sc_enablemodule)(struct prcm_softc *sc, int mod); + void (*sc_setclock)(struct prcm_softc *sc, + int clock, int speed); + uint32_t cm1_avail; + uint32_t cm2_avail; +}; + +int prcm_match(struct device *, void *, void *); +void prcm_attach(struct device *, struct device *, void *); +int prcm_setup_dpll5(struct prcm_softc *); +uint32_t prcm_v3_bit(int mod); +uint32_t prcm_am335x_clkctrl(int mod); + +void prcm_am335x_enablemodule(struct prcm_softc *, int); +void prcm_am335x_setclock(struct prcm_softc *, int, int); + +void prcm_v3_setup(struct prcm_softc *); +void prcm_v3_enablemodule(struct prcm_softc *, int); +void prcm_v3_setclock(struct prcm_softc *, int, int); + +void prcm_v4_enablemodule(struct prcm_softc *, int); +int prcm_v4_hsusbhost_activate(int); +int prcm_v4_hsusbhost_set_source(int, int); + +struct cfattach prcm_ca = { + sizeof (struct prcm_softc), NULL, prcm_attach +}; + +struct cfdriver prcm_cd = { + NULL, "prcm", DV_DULL +}; + +void +prcm_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct prcm_softc *sc = (struct prcm_softc *) self; + u_int32_t reg; + + sc->sc_iot = aa->aa_iot; + + switch (board_id) { + case BOARD_ID_AM335X_BEAGLEBONE: + sc->sc_setup = NULL; + sc->sc_enablemodule = prcm_am335x_enablemodule; + sc->sc_setclock = prcm_am335x_setclock; + break; + case BOARD_ID_OMAP3_BEAGLE: + case BOARD_ID_OMAP3_OVERO: + sc->sc_setup = prcm_v3_setup; + sc->sc_enablemodule = prcm_v3_enablemodule; + sc->sc_setclock = prcm_v3_setclock; + break; + case BOARD_ID_OMAP4_PANDA: + sc->sc_setup = NULL; + sc->sc_enablemodule = prcm_v4_enablemodule; + sc->sc_setclock = NULL; + sc->cm1_avail = 1; + sc->cm2_avail = 1; + break; + } + + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_prcm)) + panic("prcm_attach: bus_space_map failed!"); + + if (sc->cm1_avail && + bus_space_map(sc->sc_iot, aa->aa_dev->mem[1].addr, + aa->aa_dev->mem[1].size, 0, &sc->sc_cm1)) + panic("prcm_attach: bus_space_map failed!"); + + if (sc->cm2_avail && + bus_space_map(sc->sc_iot, aa->aa_dev->mem[2].addr, + aa->aa_dev->mem[2].size, 0, &sc->sc_cm2)) + panic("prcm_attach: bus_space_map failed!"); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, PRCM_REVISION); + printf(" rev %d.%d\n", reg >> 4 & 0xf, reg & 0xf); + + if (sc->sc_setup != NULL) + sc->sc_setup(sc); +} + +void +prcm_v3_setup(struct prcm_softc *sc) +{ + /* Setup the 120MHZ DPLL5 clock, to be used by USB. */ + prcm_setup_dpll5(sc); + + prcm_fmask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FMASK; + prcm_imask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IMASK; + prcm_fmask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FADDR; + prcm_imask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IADDR; + + prcm_fmask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FMASK; + prcm_imask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IMASK; + prcm_fmask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FADDR; + prcm_imask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IADDR; + + prcm_fmask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FMASK; + prcm_imask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IMASK; + prcm_fmask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FADDR; + prcm_imask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IADDR; + + prcm_fmask_mask[PRCM_REG_WKUP] = PRCM_REG_WKUP_FMASK; + prcm_imask_mask[PRCM_REG_WKUP] = PRCM_REG_WKUP_IMASK; + prcm_fmask_addr[PRCM_REG_WKUP] = PRCM_REG_WKUP_FADDR; + prcm_imask_addr[PRCM_REG_WKUP] = PRCM_REG_WKUP_IADDR; + + prcm_fmask_mask[PRCM_REG_PER] = PRCM_REG_PER_FMASK; + prcm_imask_mask[PRCM_REG_PER] = PRCM_REG_PER_IMASK; + prcm_fmask_addr[PRCM_REG_PER] = PRCM_REG_PER_FADDR; + prcm_imask_addr[PRCM_REG_PER] = PRCM_REG_PER_IADDR; + + prcm_fmask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FMASK; + prcm_imask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IMASK; + prcm_fmask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FADDR; + prcm_imask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IADDR; +} + +void +prcm_setclock(int clock, int speed) +{ + struct prcm_softc *sc = prcm_cd.cd_devs[0]; + + if (!sc->sc_setclock) + panic("%s: not initialised!", __func__); + + sc->sc_setclock(sc, clock, speed); +} + +void +prcm_am335x_setclock(struct prcm_softc *sc, int clock, int speed) +{ + u_int32_t oreg, reg, mask; + + /* set CLKSEL register */ + if (clock == 1) { + oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, + PRCM_AM335X_CLKSEL_TIMER2_CLK); + mask = 3; + reg = oreg & ~mask; + reg |=0x02; + bus_space_write_4(sc->sc_iot, sc->sc_prcm, + PRCM_AM335X_CLKSEL_TIMER2_CLK, reg); + } else if (clock == 2) { + oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, + PRCM_AM335X_CLKSEL_TIMER3_CLK); + mask = 3; + reg = oreg & ~mask; + reg |=0x02; + bus_space_write_4(sc->sc_iot, sc->sc_prcm, + PRCM_AM335X_CLKSEL_TIMER3_CLK, reg); + } +} + +void +prcm_v3_setclock(struct prcm_softc *sc, int clock, int speed) +{ + u_int32_t oreg, reg, mask; + + if (clock == 1) { + oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP); + mask = 1; + reg = (oreg &~mask) | (speed & mask); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP, reg); + } else if (clock >= 2 && clock <= 9) { + int shift = (clock-2); + oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER); + mask = 1 << (shift); + reg = (oreg & ~mask) | ( (speed << shift) & mask); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER, reg); + } else + panic("%s: invalid clock %d", __func__, clock); +} + +uint32_t +prcm_v3_bit(int mod) +{ + switch(mod) { + case PRCM_MMC0: + return PRCM_CLK_EN_MMC1; + case PRCM_MMC1: + return PRCM_CLK_EN_MMC2; + case PRCM_MMC2: + return PRCM_CLK_EN_MMC3; + case PRCM_USB: + return PRCM_CLK_EN_USB; + case PRCM_GPIO0: + return PRCM_CLK_EN_GPIO1; + case PRCM_GPIO1: + return PRCM_CLK_EN_GPIO2; + case PRCM_GPIO2: + return PRCM_CLK_EN_GPIO3; + case PRCM_GPIO3: + return PRCM_CLK_EN_GPIO4; + case PRCM_GPIO4: + return PRCM_CLK_EN_GPIO5; + case PRCM_GPIO5: + return PRCM_CLK_EN_GPIO6; + default: + panic("%s: module not found\n", __func__); + } +} + +uint32_t +prcm_am335x_clkctrl(int mod) +{ + switch(mod) { + case PRCM_TIMER2: + return PRCM_AM335X_TIMER2_CLKCTRL; + case PRCM_TIMER3: + return PRCM_AM335X_TIMER3_CLKCTRL; + case PRCM_MMC0: + return PRCM_AM335X_MMC0_CLKCTRL; + case PRCM_MMC1: + return PRCM_AM335X_MMC1_CLKCTRL; + case PRCM_MMC2: + return PRCM_AM335X_MMC2_CLKCTRL; + case PRCM_USB: + return PRCM_AM335X_USB0_CLKCTRL; + case PRCM_GPIO0: + return PRCM_AM335X_GPIO0_CLKCTRL; + case PRCM_GPIO1: + return PRCM_AM335X_GPIO1_CLKCTRL; + case PRCM_GPIO2: + return PRCM_AM335X_GPIO2_CLKCTRL; + case PRCM_GPIO3: + return PRCM_AM335X_GPIO3_CLKCTRL; + case PRCM_TPCC: + return PRCM_AM335X_TPCC_CLKCTRL; + case PRCM_TPTC0: + return PRCM_AM335X_TPTC0_CLKCTRL; + case PRCM_TPTC1: + return PRCM_AM335X_TPTC1_CLKCTRL; + case PRCM_TPTC2: + return PRCM_AM335X_TPTC2_CLKCTRL; + case PRCM_I2C0: + return PRCM_AM335X_I2C0_CLKCTRL; + case PRCM_I2C1: + return PRCM_AM335X_I2C1_CLKCTRL; + case PRCM_I2C2: + return PRCM_AM335X_I2C2_CLKCTRL; + default: + panic("%s: module not found\n", __func__); + } +} + +void +prcm_enablemodule(int mod) +{ + struct prcm_softc *sc = prcm_cd.cd_devs[0]; + + if (!sc->sc_enablemodule) + panic("%s: not initialised!", __func__); + + sc->sc_enablemodule(sc, mod); +} + +void +prcm_am335x_enablemodule(struct prcm_softc *sc, int mod) +{ + uint32_t clkctrl; + int reg; + + /*set enable bits in CLKCTRL register */ + reg = prcm_am335x_clkctrl(mod); + clkctrl = bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg); + clkctrl &=~AM335X_CLKCTRL_MODULEMODE_MASK; + clkctrl |= AM335X_CLKCTRL_MODULEMODE_ENABLE; + bus_space_write_4(sc->sc_iot, sc->sc_prcm, reg, clkctrl); + + /* wait until module is enabled */ + while (bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg) & 0x30000) + ; +} + +void +prcm_v3_enablemodule(struct prcm_softc *sc, int mod) +{ + uint32_t bit; + uint32_t fclk, iclk, fmask, imask, mbit; + int freg, ireg, reg; + + bit = prcm_v3_bit(mod); + reg = bit >> 5; + + freg = prcm_fmask_addr[reg]; + ireg = prcm_imask_addr[reg]; + fmask = prcm_fmask_mask[reg]; + imask = prcm_imask_mask[reg]; + + mbit = 1 << (bit & 0x1f); + if (fmask & mbit) { /* dont access the register if bit isn't present */ + fclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, freg); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, freg, fclk | mbit); + } + if (imask & mbit) { /* dont access the register if bit isn't present */ + iclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, ireg); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, ireg, iclk | mbit); + } + printf("\n"); +} + +void +prcm_v4_enablemodule(struct prcm_softc *sc, int mod) +{ + switch (mod) { + case PRCM_MMC0: + break; + case PRCM_USBP1_PHY: + case PRCM_USBP2_PHY: + prcm_v4_hsusbhost_set_source(mod, 0); + case PRCM_USB: + case PRCM_USBTLL: + case PRCM_USBP1_UTMI: + case PRCM_USBP1_HSIC: + case PRCM_USBP2_UTMI: + case PRCM_USBP2_HSIC: + prcm_v4_hsusbhost_activate(mod); + return; + case PRCM_GPIO0: + case PRCM_GPIO1: + case PRCM_GPIO2: + case PRCM_GPIO3: + case PRCM_GPIO4: + case PRCM_GPIO5: + /* XXX */ + break; + default: + panic("%s: module not found\n", __func__); + } +} + +int +prcm_v4_hsusbhost_activate(int type) +{ + struct prcm_softc *sc = prcm_cd.cd_devs[0]; + uint32_t i; + uint32_t clksel_reg_off; + uint32_t clksel, oclksel; + + switch (type) { + case PRCM_USB: + case PRCM_USBP1_PHY: + case PRCM_USBP2_PHY: + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58; + clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off); + oclksel = clksel; + /* Enable the module and also enable the optional func clocks */ + if (type == PRCM_USB) { + clksel &= ~O4_CLKCTRL_MODULEMODE_MASK; + clksel |= /*O4_CLKCTRL_MODULEMODE_ENABLE*/2; + + clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */ + } + + break; + + default: + panic("%s: invalid type %d", __func__, type); + return (EINVAL); + } + bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel); + + /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */ + for (i = 0; i < O4_MAX_MODULE_ENABLE_WAIT; i++) { + clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off); + if ((clksel & O4_CLKCTRL_IDLEST_MASK) == O4_CLKCTRL_IDLEST_ENABLED) + break; + } + + /* Check the enabled state */ + if ((clksel & O4_CLKCTRL_IDLEST_MASK) != O4_CLKCTRL_IDLEST_ENABLED) { + printf("Error: HERE failed to enable module with clock %d\n", type); + printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel); + return (ETIMEDOUT); + } + + return (0); +} + +int +prcm_v4_hsusbhost_set_source(int clk, int clksrc) +{ + struct prcm_softc *sc = prcm_cd.cd_devs[0]; + uint32_t clksel_reg_off; + uint32_t clksel; + unsigned int bit; + + if (clk == PRCM_USBP1_PHY) + bit = 24; + else if (clk != PRCM_USBP2_PHY) + bit = 25; + else + return (-EINVAL); + + /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ + clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58; + clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off); + + /* XXX: Set the clock source to either external or internal */ + if (clksrc == 0) + clksel |= (0x1 << bit); + else + clksel &= ~(0x1 << bit); + + bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel); + + return (0); +} + +/* + * OMAP35xx Power, Reset, and Clock Management Reference Guide + * (sprufa5.pdf) and AM/DM37x Multimedia Device Technical Reference + * Manual (sprugn4h.pdf) note that DPLL5 provides a 120MHz clock for + * peripheral domain modules (page 107 and page 302). + * The reference clock for DPLL5 is DPLL5_ALWON_FCLK which is + * SYS_CLK, running at 13MHz. + */ +int +prcm_setup_dpll5(struct prcm_softc *sc) +{ + uint32_t val; + + /* + * We need to set the multiplier and divider values for PLL. + * To end up with 120MHz we take SYS_CLK, divide by it and multiply + * with 120 (sprugn4h.pdf, 13.4.11.4.1 SSC Configuration) + */ + val = ((120 & 0x7ff) << 8) | ((SYS_CLK - 1) & 0x7f); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL4_PLL, val); + + /* Clock divider from the PLL to the 120MHz clock. */ + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL5_PLL, val); + + /* + * spruf98o.pdf, page 2319: + * PERIPH2_DPLL_FREQSEL is 0x7 1.75MHz to 2.1MHz + * EN_PERIPH2_DPLL is 0x7 + */ + val = (7 << 4) | (7 << 0); + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKEN2_PLL, val); + + /* Disable the interconnect clock auto-idle. */ + bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_AUTOIDLE2_PLL, 0x0); + + /* Wait until DPLL5 is locked and there's clock activity. */ + while ((val = bus_space_read_4(sc->sc_iot, sc->sc_prcm, + CM_IDLEST_CKGEN) & 0x01) == 0x00) { +#ifdef DIAGNOSTIC + printf("CM_IDLEST_PLL = 0x%08x\n", val); +#endif + } + + return 0; +} diff --git a/prcmvar.h b/prcmvar.h new file mode 100644 index 0000000..736bcfc --- /dev/null +++ b/prcmvar.h @@ -0,0 +1,57 @@ +/* $OpenBSD: prcmvar.h,v 1.5 2014/03/18 07:34:17 syl Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void prcm_setclock(int clock, int speed); +void prcm_enablemodule(int mod); +void prcm_disablemodule(int mod); + +#define PRCM_CLK_SPEED_32 0 +#define PRCM_CLK_SPEED_SYS 1 + +enum PRCM_MODULES { + PRCM_TIMER0, + PRCM_TIMER1, + PRCM_TIMER2, + PRCM_TIMER3, + PRCM_GPIO0, + PRCM_GPIO1, + PRCM_GPIO2, + PRCM_GPIO3, + PRCM_GPIO4, + PRCM_GPIO5, + PRCM_TPCC, + PRCM_TPTC0, + PRCM_TPTC1, + PRCM_TPTC2, + PRCM_MMC0, + PRCM_MMC1, + PRCM_MMC2, + PRCM_USB, + PRCM_USBTLL, + PRCM_USBP1_PHY, + PRCM_USBP1_UTMI, + PRCM_USBP1_HSIC, + PRCM_USBP2_PHY, + PRCM_USBP2_UTMI, + PRCM_USBP2_HSIC, + PRCM_I2C0, + PRCM_I2C1, + PRCM_I2C2, +}; + +#define PRCM_REG_MAX 6 +/* need interface for CM_AUTOIDLE */ diff --git a/sitara_cm.c b/sitara_cm.c new file mode 100644 index 0000000..4958a4f --- /dev/null +++ b/sitara_cm.c @@ -0,0 +1,394 @@ +/* $OpenBSD: sitara_cm.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */ +/* $NetBSD: sitara_cm.c,v 1.1 2013/04/17 14:31:02 bouyer Exp $ */ +/* + * 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. + */ + +/* + * SCM - System Control Module + * + * 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 sitara_cm_padconf structs + * call ti_padconf_devmap to be located somewhere in the kernel. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void sitara_cm_attach(struct device *parent, struct device *self, void *aux); + +struct sitara_cm_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct cfattach sitaracm_ca = { + sizeof (struct sitara_cm_softc), NULL, sitara_cm_attach +}; + +struct cfdriver sitaracm_cd = { + NULL, "sitaracm", DV_DULL +}; + +static struct sitara_cm_softc *sitara_cm_sc = NULL; + +#define sitara_cm_read_2(sc, reg) \ + bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define sitara_cm_write_2(sc, reg, val) \ + bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define sitara_cm_read_4(sc, reg) \ + bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define sitara_cm_write_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (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. + */ +extern const struct sitara_cm_device sitara_cm_dev; + + +/** + * sitara_cm_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 sitara_cm_padconf* +sitara_cm_padconf_from_name(const char *ballname) +{ + const struct sitara_cm_padconf *padconf; + + padconf = sitara_cm_dev.padconf; + while (padconf->ballname != NULL) { + if (strcmp(ballname, padconf->ballname) == 0) + return(padconf); + padconf++; + } + + return (NULL); +} + +/** + * sitara_cm_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 +sitara_cm_padconf_set_internal(struct sitara_cm_softc *sc, + const struct sitara_cm_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 & sitara_cm_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("%s: Invalid mux mode \"%s\"\n", __func__, muxmode); + return (EINVAL); + } + + /* set the mux mode */ + reg_val |= (uint16_t)(mode & sitara_cm_dev.padconf_muxmode_mask); + + /* write the register value (16-bit writes) */ + sitara_cm_write_2(sc, padconf->reg_off, reg_val); + + return (0); +} + +/** + * sitara_cm_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 +sitara_cm_padconf_set(const char *padname, const char *muxmode, unsigned int state) +{ + const struct sitara_cm_padconf *padconf; + + if (!sitara_cm_sc) + return (ENXIO); + + /* find the pin in the devmap */ + padconf = sitara_cm_padconf_from_name(padname); + if (padconf == NULL) + return (EINVAL); + + return ( + sitara_cm_padconf_set_internal(sitara_cm_sc, padconf, muxmode, state) + ); +} + +/** + * sitara_cm_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 +sitara_cm_padconf_get(const char *padname, const char **muxmode, + unsigned int *state) +{ + const struct sitara_cm_padconf *padconf; + uint16_t reg_val; + + if (!sitara_cm_sc) + return (ENXIO); + + /* find the pin in the devmap */ + padconf = sitara_cm_padconf_from_name(padname); + if (padconf == NULL) + return (EINVAL); + + /* read the register value (16-bit reads) */ + reg_val = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off); + + /* save the state */ + if (state) + *state = (reg_val & sitara_cm_dev.padconf_sate_mask); + + /* save the mode */ + if (muxmode) { + *muxmode = padconf->muxmodes[ + (reg_val & sitara_cm_dev.padconf_muxmode_mask) + ]; + } + + return (0); +} + +/** + * sitara_cm_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 +sitara_cm_padconf_set_gpiomode(uint32_t gpio, unsigned int state) +{ + const struct sitara_cm_padconf *padconf; + uint16_t reg_val; + + if (!sitara_cm_sc) + return (ENXIO); + + /* find the gpio pin in the padconf array */ + padconf = sitara_cm_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 & sitara_cm_dev.padconf_sate_mask); + + /* set the mux mode */ + reg_val |= + (uint16_t)(padconf->gpio_mode & sitara_cm_dev.padconf_muxmode_mask); + + /* write the register value (16-bit writes) */ + sitara_cm_write_2(sitara_cm_sc, padconf->reg_off, reg_val); + + return (0); +} + +/** + * sitara_cm_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 +sitara_cm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state) +{ + const struct sitara_cm_padconf *padconf; + uint16_t reg_val; + + if (!sitara_cm_sc) + return (ENXIO); + + /* find the gpio pin in the padconf array */ + padconf = sitara_cm_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 = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off); + + /* + * check to make sure the pins is configured as GPIO in the + * first state + */ + if ((reg_val & sitara_cm_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 & sitara_cm_dev.padconf_sate_mask); + + return (0); +} + + +int +sitara_cm_reg_read_4(uint32_t reg, uint32_t *val) +{ + if (!sitara_cm_sc) + return (ENXIO); + + *val = sitara_cm_read_4(sitara_cm_sc, reg); + return (0); +} + +int +sitara_cm_reg_write_4(uint32_t reg, uint32_t val) +{ + if (!sitara_cm_sc) + return (ENXIO); + + sitara_cm_write_4(sitara_cm_sc, reg, val); + return (0); +} + +void +sitara_cm_attach(struct device *parent, struct device *self, void *aux) +{ + struct sitara_cm_softc *sc = (struct sitara_cm_softc *)self; + struct armv7_attach_args *aa = aux; + uint32_t rev; + + if (sitara_cm_sc) + panic("sitara_cm_attach: already attached"); + + sc->sc_iot = aa->aa_iot; + + if (bus_space_map(aa->aa_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh) != 0) + panic("%s: bus_space_map failed!\n", __func__); + + sitara_cm_sc = sc; + + if (sitara_cm_reg_read_4(OMAP2SCM_REVISION, &rev) != 0) + panic("sitara_cm_attach: read revision"); + printf(": control module, rev %d.%d\n", + SCM_REVISION_MAJOR(rev), SCM_REVISION_MINOR(rev)); +} diff --git a/sitara_cm.h b/sitara_cm.h new file mode 100644 index 0000000..96be967 --- /dev/null +++ b/sitara_cm.h @@ -0,0 +1,78 @@ +/* $OpenBSD: sitara_cm.h,v 1.1 2013/09/04 14:38:32 patrick Exp $ */ +/* $NetBSD: sitara_cm.h,v 1.1 2013/04/17 14:31:02 bouyer Exp $ */ +/* + * 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. + * + */ + + +/** + * Functions to configure the PIN multiplexing on the chip. + * + * This is different from the GPIO module in that it is used to configure the + * pins between modules not just GPIO input output. + * + */ +#ifndef _OMAP_SCM_H_ +#define _OMAP_SCM_H_ + +struct sitara_cm_padconf { + uint16_t reg_off; + uint16_t gpio_pin; + uint16_t gpio_mode; + const char *ballname; + const char *muxmodes[8]; +}; + +struct sitara_cm_padstate { + const char *state; + uint16_t reg; +}; + +struct sitara_cm_device { + uint16_t padconf_muxmode_mask; + uint16_t padconf_sate_mask; + struct sitara_cm_padstate *padstate; + struct sitara_cm_padconf *padconf; +}; + +int sitara_cm_padconf_set(const char *padname, const char *muxmode, + unsigned int state); +int sitara_cm_padconf_get(const char *padname, const char **muxmode, + unsigned int *state); +int sitara_cm_padconf_set_gpiomode(uint32_t gpio, unsigned int state); +int sitara_cm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state); +int sitara_cm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags); +void sitara_cm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags); +int sitara_cm_reg_read_4(uint32_t reg, uint32_t *val); +int sitara_cm_reg_write_4(uint32_t reg, uint32_t val); + +#endif /* _OMAP_SCM_H_ */ diff --git a/sitara_cmreg.h b/sitara_cmreg.h new file mode 100644 index 0000000..65aeeeb --- /dev/null +++ b/sitara_cmreg.h @@ -0,0 +1,46 @@ +/* $OpenBSD: sitara_cmreg.h,v 1.1 2013/09/04 14:38:32 patrick Exp $ */ +/* $NetBSD: sitara_cmreg.h,v 1.1 2013/04/17 15:04:39 bouyer Exp $ */ + +/* + * Copyright (c) 2013 Manuel Bouyer. 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. + */ + +/* register definitions for the Control module found in the + * Texas Instrument AM335x SOC + */ + +#ifndef _OMAP2SCMREG_H +#define _OMAP2SCMREG_H + +#define OMAP2SCM_REVISION 0x0000 +#define SCM_REVISION_SCHEME(x) (((x) & 0xc0000000) >> 30) +#define SCM_REVISION_FUNC(x) (((x) & 0x0fff0000) >> 16) +#define SCM_REVISION_RTL(x) (((x) & 0x0000f800) >> 11) +#define SCM_REVISION_MAJOR(x) (((x) & 0x00000700) >> 8) +#define SCM_REVISION_CUSTOM(x) (((x) & 0x000000c0) >> 6) +#define SCM_REVISION_MINOR(x) (((x) & 0x0000001f) >> 0) + +#define OMAP2SCM_MAC_ID0_LO 0x630 +#define OMAP2SCM_MAC_ID0_HI 0x634 + +#endif /* _OMAP2SCMREG_H */ diff --git a/ti_iic.c b/ti_iic.c new file mode 100644 index 0000000..33ad915 --- /dev/null +++ b/ti_iic.c @@ -0,0 +1,589 @@ +/* $OpenBSD: ti_iic.c,v 1.2 2014/03/18 14:23:52 rapha Exp $ */ +/* $NetBSD: ti_iic.c,v 1.4 2013/04/25 13:04:27 rkujawa Exp $ */ + +/* + * Copyright (c) 2013 Manuel Bouyer. 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. + */ + +/*- + * Copyright (c) 2012 Jared D. McNeill + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifndef AM335X_I2C_SLAVE_ADDR +#define AM335X_I2C_SLAVE_ADDR 0x01 +#endif + +#ifdef I2CDEBUG +#define DPRINTF(args) printf args +#else +#define DPRINTF(args) +#endif + +/* operation in progress */ +typedef enum { + TI_I2CREAD, + TI_I2CWRITE, + TI_I2CDONE, + TI_I2CERROR +} ti_i2cop_t; + +struct ti_iic_softc { + struct device sc_dev; + struct i2c_controller sc_ic; + struct rwlock sc_buslock; + struct device *sc_i2cdev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + void *sc_ih; + ti_i2cop_t sc_op; + int sc_buflen; + int sc_bufidx; + char *sc_buf; + + int sc_rxthres; + int sc_txthres; +}; + + +#define I2C_READ_REG(sc, reg) \ + bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define I2C_READ_DATA(sc) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, AM335X_I2C_DATA); +#define I2C_WRITE_REG(sc, reg, val) \ + bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define I2C_WRITE_DATA(sc, val) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, AM335X_I2C_DATA, (val)) + +#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) + +static void ti_iic_attach(struct device *, struct device *, void *); +static int ti_iic_intr(void *); + +static int ti_iic_acquire_bus(void *, int); +static void ti_iic_release_bus(void *, int); +static int ti_iic_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); + +static int ti_iic_reset(struct ti_iic_softc *); +static int ti_iic_op(struct ti_iic_softc *, i2c_addr_t, ti_i2cop_t, + uint8_t *, size_t, int); +static void ti_iic_handle_intr(struct ti_iic_softc *, uint32_t); +static void ti_iic_do_read(struct ti_iic_softc *, uint32_t); +static void ti_iic_do_write(struct ti_iic_softc *, uint32_t); + +static int ti_iic_wait(struct ti_iic_softc *, uint16_t, uint16_t, int); +static uint32_t ti_iic_stat(struct ti_iic_softc *, uint32_t); +static int ti_iic_flush(struct ti_iic_softc *); + +struct cfattach tiiic_ca = { + sizeof (struct ti_iic_softc), NULL, ti_iic_attach +}; + +struct cfdriver tiiic_cd = { + NULL, "tiiic", DV_DULL +}; + +static void +ti_iic_attach(struct device *parent, struct device *self, void *args) +{ + struct ti_iic_softc *sc = (struct ti_iic_softc *)self; + struct armv7_attach_args *aa = args; + struct i2cbus_attach_args iba; + uint16_t rev; + const char *mode; + u_int state; + char buf[20]; + char *pin; + /* BBB specific pin names */ + char *pins[6] = {"I2C0_SDA", "I2C0_SCL", + "SPIO_D1", "SPI0_CS0", + "UART1_CTSn", "UART1_RTSn"}; + + sc->sc_iot = aa->aa_iot; + rw_init(&sc->sc_buslock, "tiiilk"); + + sc->sc_rxthres = sc->sc_txthres = 4; + + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!"); + + sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_NET, + ti_iic_intr, sc, DEVNAME(sc)); + + prcm_enablemodule(PRCM_I2C0 + aa->aa_dev->unit); + + if (board_id == BOARD_ID_AM335X_BEAGLEBONE) { + pin = pins[aa->aa_dev->unit * 2]; + snprintf(buf, sizeof buf, "I2C%d_SDA", aa->aa_dev->unit); + if (sitara_cm_padconf_set(pin, buf, + (0x01 << 4) | (0x01 << 5) | (0x01 << 6)) != 0) { + printf(": can't switch %s pad\n", buf); + return; + } + if (sitara_cm_padconf_get(pin, &mode, &state) == 0) { + printf(": %s state %d ", mode, state); + } + + pin = pins[aa->aa_dev->unit * 2 + 1]; + snprintf(buf, sizeof buf, "I2C%d_SCL", aa->aa_dev->unit); + if (sitara_cm_padconf_set(pin, buf, + (0x01 << 4) | (0x01 << 5) | (0x01 << 6)) != 0) { + printf(": can't switch %s pad\n", buf); + return; + } + if (sitara_cm_padconf_get(pin, &mode, &state) == 0) { + printf(": %s state %d ", mode, state); + } + } + + rev = I2C_READ_REG(sc, AM335X_I2C_REVNB_LO); + printf(" rev %d.%d\n", + (int)I2C_REVNB_LO_MAJOR(rev), + (int)I2C_REVNB_LO_MINOR(rev)); + + ti_iic_reset(sc); + ti_iic_flush(sc); + + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_acquire_bus = ti_iic_acquire_bus; + sc->sc_ic.ic_release_bus = ti_iic_release_bus; + sc->sc_ic.ic_exec = ti_iic_exec; + + bzero(&iba, sizeof iba); + iba.iba_name = "iic"; + iba.iba_tag = &sc->sc_ic; + (void) config_found(&sc->sc_dev, &iba, iicbus_print); +} + +static int +ti_iic_intr(void *arg) +{ + struct ti_iic_softc *sc = arg; + uint32_t stat; + + DPRINTF(("ti_iic_intr\n")); + stat = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS); + I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS, stat); + DPRINTF(("ti_iic_intr pre handle sc->sc_op eq %#x\n", sc->sc_op)); + + ti_iic_handle_intr(sc, stat); + + if (sc->sc_op == TI_I2CERROR || sc->sc_op == TI_I2CDONE) { + DPRINTF(("ti_iic_intr post handle sc->sc_op %#x\n", sc->sc_op)); + wakeup(&sc->sc_dev); + } + + DPRINTF(("ti_iic_intr status 0x%x\n", stat)); + + return 1; +} + +static int +ti_iic_acquire_bus(void *opaque, int flags) +{ + struct ti_iic_softc *sc = opaque; + + if (flags & I2C_F_POLL) + return 0; + + return (rw_enter(&sc->sc_buslock, RW_WRITE)); +} + +static void +ti_iic_release_bus(void *opaque, int flags) +{ + struct ti_iic_softc *sc = opaque; + + if (flags & I2C_F_POLL) + return; + + rw_exit(&sc->sc_buslock); +} + +static int +ti_iic_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) +{ + struct ti_iic_softc *sc = opaque; + int err; + + + DPRINTF(("ti_iic_exec: op 0x%x cmdlen %zd len %zd flags 0x%x\n", + op, cmdlen, len, flags)); + +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + if (cmdlen > 0) { + err = ti_iic_op(sc, addr, TI_I2CWRITE, __UNCONST(cmdbuf), + cmdlen, (I2C_OP_READ_P(op) ? 0 : I2C_F_STOP) | flags); + if (err) + goto done; + } + if (I2C_OP_STOP_P(op)) + flags |= I2C_F_STOP; + + /* + * I2C controller doesn't allow for zero-byte transfers. + */ + if (len == 0) + goto done; + + if (I2C_OP_READ_P(op)) + err = ti_iic_op(sc, addr, TI_I2CREAD, buf, len, flags); + else + err = ti_iic_op(sc, addr, TI_I2CWRITE, buf, len, flags); + +done: + if (err) + ti_iic_reset(sc); + + ti_iic_flush(sc); + + DPRINTF(("ti_iic_exec: done %d\n", err)); + return err; +} + +static int +ti_iic_reset(struct ti_iic_softc *sc) +{ + uint32_t psc, scll, sclh; + int i; + + DPRINTF(("ti_iic_reset\n")); + + /* Disable */ + I2C_WRITE_REG(sc, AM335X_I2C_CON, 0); + /* Soft reset */ + I2C_WRITE_REG(sc, AM335X_I2C_SYSC, I2C_SYSC_SRST); + delay(1000); + /* enable so that we can check for reset complete */ + I2C_WRITE_REG(sc, AM335X_I2C_CON, I2C_CON_EN); + delay(1000); + for (i = 0; i < 1000; i++) { /* 1s delay for reset */ + if (I2C_READ_REG(sc, AM335X_I2C_SYSS) & I2C_SYSS_RDONE) + break; + } + /* Disable again */ + I2C_WRITE_REG(sc, AM335X_I2C_CON, 0); + delay(50000); + + if (i >= 1000) { + printf("%s: couldn't reset module\n", DEVNAME(sc)); + return 1; + } + + /* XXX standard speed only */ + psc = 3; + scll = 53; + sclh = 55; + + /* Clocks */ + I2C_WRITE_REG(sc, AM335X_I2C_PSC, psc); + I2C_WRITE_REG(sc, AM335X_I2C_SCLL, scll); + I2C_WRITE_REG(sc, AM335X_I2C_SCLH, sclh); + + /* Own I2C address */ + I2C_WRITE_REG(sc, AM335X_I2C_OA, AM335X_I2C_SLAVE_ADDR); + + /* 5 bytes fifo */ + I2C_WRITE_REG(sc, AM335X_I2C_BUF, + I2C_BUF_RXTRSH(sc->sc_rxthres) | I2C_BUF_TXTRSH(sc->sc_txthres)); + + /* Enable */ + I2C_WRITE_REG(sc, AM335X_I2C_CON, I2C_CON_EN); + + return 0; +} + +static int +ti_iic_op(struct ti_iic_softc *sc, i2c_addr_t addr, ti_i2cop_t op, + uint8_t *buf, size_t buflen, int flags) +{ + uint16_t con, stat, mask; + int err, retry; + + KASSERT(op == TI_I2CREAD || op == TI_I2CWRITE); + DPRINTF(("ti_iic_op: addr %#x op %#x buf %p buflen %#x flags %#x\n", + addr, op, buf, (unsigned int) buflen, flags)); + + mask = I2C_IRQSTATUS_ARDY | I2C_IRQSTATUS_NACK | I2C_IRQSTATUS_AL; + if (op == TI_I2CREAD) + mask |= I2C_IRQSTATUS_RDR | I2C_IRQSTATUS_RRDY; + else + mask |= I2C_IRQSTATUS_XDR | I2C_IRQSTATUS_XRDY; + + err = ti_iic_wait(sc, I2C_IRQSTATUS_BB, 0, flags); + if (err) { + DPRINTF(("ti_iic_op: wait error %d\n", err)); + return err; + } + + con = I2C_CON_EN; + con |= I2C_CON_MST; + con |= I2C_CON_STT; + if (flags & I2C_F_STOP) + con |= I2C_CON_STP; + if (addr & ~0x7f) + con |= I2C_CON_XSA; + if (op == TI_I2CWRITE) + con |= I2C_CON_TRX; + + sc->sc_op = op; + sc->sc_buf = buf; + sc->sc_buflen = buflen; + sc->sc_bufidx = 0; + + I2C_WRITE_REG(sc, + AM335X_I2C_CON, I2C_CON_EN | I2C_CON_MST | I2C_CON_STP); + DPRINTF(("ti_iic_op: op %d con 0x%x ", op, con)); + I2C_WRITE_REG(sc, AM335X_I2C_CNT, buflen); + I2C_WRITE_REG(sc, AM335X_I2C_SA, (addr & I2C_SA_MASK)); + DPRINTF(("SA 0x%x len %d\n", + I2C_READ_REG(sc, AM335X_I2C_SA), I2C_READ_REG(sc, AM335X_I2C_CNT))); + + if ((flags & I2C_F_POLL) == 0) { + /* clear any pending interrupt */ + I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS, + I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS)); + /* and enable */ + I2C_WRITE_REG(sc, AM335X_I2C_IRQENABLE_SET, mask); + } + /* start transfer */ + I2C_WRITE_REG(sc, AM335X_I2C_CON, con); + + if ((flags & I2C_F_POLL) == 0) { + /* and wait for completion */ + DPRINTF(("ti_iic_op waiting, op %#x\n", sc->sc_op)); + while (sc->sc_op == op) { + if (tsleep(&sc->sc_dev, PWAIT, "tiiic", 500) + == EWOULDBLOCK) { + /* timeout */ + op = TI_I2CERROR; + } + } + DPRINTF(("ti_iic_op waiting done, op %#x\n", sc->sc_op)); + + /* disable interrupts */ + I2C_WRITE_REG(sc, AM335X_I2C_IRQENABLE_CLR, 0xffff); + } else { + /* poll for completion */ + DPRINTF(("ti_iic_op polling, op %x\n", sc->sc_op)); + while (sc->sc_op == op) { + stat = ti_iic_stat(sc, mask); + DPRINTF(("ti_iic_op stat 0x%x\n", stat)); + if (stat == 0) /* timeout */ + sc->sc_op = TI_I2CERROR; + else + ti_iic_handle_intr(sc, stat); + I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS, stat); + } + DPRINTF(("ti_iic_op polling done, op now %x\n", sc->sc_op)); + } + retry = 10000; + I2C_WRITE_REG(sc, AM335X_I2C_CON, 0); + while (I2C_READ_REG(sc, AM335X_I2C_CON) & I2C_CON_MST) { + delay(100); + if (--retry == 0) + break; + } + + return (sc->sc_op == TI_I2CDONE) ? 0 : EIO; +} + +static void +ti_iic_handle_intr(struct ti_iic_softc *sc, uint32_t stat) +{ + KASSERT(stat != 0); + DPRINTF(("ti_iic_handle_intr stat %#x\n", stat)); + + if (stat & (I2C_IRQSTATUS_NACK|I2C_IRQSTATUS_AL)) { + sc->sc_op = TI_I2CERROR; + return; + } + if (stat & I2C_IRQSTATUS_ARDY) { + sc->sc_op = TI_I2CDONE; + return; + } + if (sc->sc_op == TI_I2CREAD) + ti_iic_do_read(sc, stat); + else if (sc->sc_op == TI_I2CWRITE) + ti_iic_do_write(sc, stat); + else + return; +} +void +ti_iic_do_read(struct ti_iic_softc *sc, uint32_t stat) +{ + int len = 0; + + DPRINTF(("ti_iic_do_read stat %#x\n", stat)); + if (stat & I2C_IRQSTATUS_RDR) { + len = I2C_READ_REG(sc, AM335X_I2C_BUFSTAT); + len = I2C_BUFSTAT_RXSTAT(len); + DPRINTF(("ti_iic_do_read receive drain len %d left %d\n", + len, I2C_READ_REG(sc, AM335X_I2C_CNT))); + } else if (stat & I2C_IRQSTATUS_RRDY) { + len = sc->sc_rxthres + 1; + DPRINTF(("ti_iic_do_read receive len %d left %d\n", + len, I2C_READ_REG(sc, AM335X_I2C_CNT))); + } + for (; + sc->sc_bufidx < sc->sc_buflen && len > 0; + sc->sc_bufidx++, len--) { + sc->sc_buf[sc->sc_bufidx] = I2C_READ_DATA(sc); + DPRINTF(("ti_iic_do_read got b[%d]=0x%x\n", sc->sc_bufidx, + sc->sc_buf[sc->sc_bufidx])); + } + DPRINTF(("ti_iic_do_read done\n")); +} + +void +ti_iic_do_write(struct ti_iic_softc *sc, uint32_t stat) +{ + int len = 0; + + DPRINTF(("ti_iic_do_write stat %#x\n", stat)); + + if (stat & I2C_IRQSTATUS_XDR) { + len = I2C_READ_REG(sc, AM335X_I2C_BUFSTAT); + len = I2C_BUFSTAT_TXSTAT(len); + DPRINTF(("ti_iic_do_write xmit drain len %d left %d\n", + len, I2C_READ_REG(sc, AM335X_I2C_CNT))); + } else if (stat & I2C_IRQSTATUS_XRDY) { + len = sc->sc_txthres + 1; + DPRINTF(("ti_iic_do_write xmit len %d left %d\n", + len, I2C_READ_REG(sc, AM335X_I2C_CNT))); + } + for (; + sc->sc_bufidx < sc->sc_buflen && len > 0; + sc->sc_bufidx++, len--) { + DPRINTF(("ti_iic_do_write send b[%d]=0x%x\n", + sc->sc_bufidx, sc->sc_buf[sc->sc_bufidx])); + I2C_WRITE_DATA(sc, sc->sc_buf[sc->sc_bufidx]); + } + DPRINTF(("ti_iic_do_write done\n")); +} + +static int +ti_iic_wait(struct ti_iic_softc *sc, uint16_t mask, uint16_t val, int flags) +{ + int retry = 10; + uint16_t v; + DPRINTF(("ti_iic_wait mask %#x val %#x flags %#x\n", mask, val, flags)); + + while (((v = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW)) & mask) != val) { + --retry; + if (retry == 0) { + printf("%s: wait timeout, mask=%#x val=%#x stat=%#x\n", + DEVNAME(sc), mask, val, v); + return EBUSY; + } + if (flags & I2C_F_POLL) + delay(50000); + else + tsleep(&sc->sc_dev, PWAIT, "tiiic", 50); + } + DPRINTF(("ti_iic_wait done retry %#x\n", retry)); + + return 0; +} + +static uint32_t +ti_iic_stat(struct ti_iic_softc *sc, uint32_t mask) +{ + uint32_t v; + int retry = 500; + DPRINTF(("ti_iic_wait mask %#x\n", mask)); + while (--retry > 0) { + v = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW) & mask; + if (v != 0) + break; + delay(100); + } + DPRINTF(("ti_iic_wait done retry %#x\n", retry)); + return v; +} + +static int +ti_iic_flush(struct ti_iic_softc *sc) +{ + DPRINTF(("ti_iic_flush\n")); +#if 0 + int retry = 1000; + uint16_t v; + + while ((v = + I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW)) & I2C_IRQSTATUS_RRDY) { + if (--retry == 0) { + printf("%s: flush timeout, stat = %#x\n", DEVNAME(sc), v); + return EBUSY; + } + (void)I2C_READ_DATA(sc); + delay(1000); + } +#endif + + I2C_WRITE_REG(sc, AM335X_I2C_CNT, 0); + return 0; +} diff --git a/ti_iicreg.h b/ti_iicreg.h new file mode 100644 index 0000000..4de67a6 --- /dev/null +++ b/ti_iicreg.h @@ -0,0 +1,136 @@ +/* $OpenBSD: ti_iicreg.h,v 1.1 2013/11/24 15:00:22 rapha Exp $ */ +/* $NetBSD: ti_iicreg.h,v 1.1 2013/04/17 14:33:06 bouyer Exp $ */ + +/* + * Copyright (c) 2013 Manuel Bouyer. 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. + */ + +/* register definitions for the i2c controller found in the + * Texas Instrument AM335x SOC + */ + +#ifndef _AM335XIICREG_H +#define _AM335XIICREG_H + +#define AM335X_I2C_REVNB_LO 0x00 +#define I2C_REVNB_LO_RTL(x) (((x) >> 11) & 0x01f) +#define I2C_REVNB_LO_MAJOR(x) (((x) >> 8) & 0x007) +#define I2C_REVNB_LO_CUSTOM(x) (((x) >> 6) & 0x003) +#define I2C_REVNB_LO_MINOR(x) (((x) >> 0) & 0x01f) +#define AM335X_I2C_REVNB_HI 0x04 +#define I2C_REVNB_HI_SCHEME(x) (((x) >> 14) & 0x003) +#define I2C_REVNB_HI_FUNC(x) (((x) >> 0) & 0xfff) +#define AM335X_I2C_SYSC 0x10 +#define I2C_SYSC_CLKACTIVITY_OCP 0x0010 +#define I2C_SYSC_CLKACTIVITY_SYSTEM 0x0020 +#define I2C_SYSC_IDLE_MASK 0x0018 +#define I2C_SYSC_IDLE_FORCE 0x0000 +#define I2C_SYSC_IDLE_SMART 0x0010 +#define I2C_SYSC_IDLE_NONE 0x0008 +#define I2C_SYSC_ENAWAKEUP 0x0004 +#define I2C_SYSC_SRST 0x0002 +#define I2C_SYSC_AUTOIDLE 0x0001 +#define AM335X_I2C_IRQSTATUS_RAW 0x24 +#define AM335X_I2C_IRQSTATUS 0x28 +#define AM335X_I2C_IRQENABLE_SET 0x2C +#define AM335X_I2C_IRQENABLE_CLR 0x30 +#define AM335X_I2C_WE 0x34 +#define I2C_IRQSTATUS_XDR 0x4000 +#define I2C_IRQSTATUS_RDR 0x2000 +#define I2C_IRQSTATUS_BB 0x1000 +#define I2C_IRQSTATUS_ROVR 0x0800 +#define I2C_IRQSTATUS_XUDF 0x0400 +#define I2C_IRQSTATUS_AAS 0x0200 +#define I2C_IRQSTATUS_BF 0x0100 +#define I2C_IRQSTATUS_AERR 0x0080 +#define I2C_IRQSTATUS_STC 0x0040 +#define I2C_IRQSTATUS_GC 0x0020 +#define I2C_IRQSTATUS_XRDY 0x0010 +#define I2C_IRQSTATUS_RRDY 0x0008 +#define I2C_IRQSTATUS_ARDY 0x0004 +#define I2C_IRQSTATUS_NACK 0x0002 +#define I2C_IRQSTATUS_AL 0x0001 +#define AM335X_I2C_DMARXENABLE_SET 0x38 +#define AM335X_I2C_DMATXENABLE_SET 0x3C +#define AM335X_I2C_DMARXENABLE_CLR 0x40 +#define I2C_DMARXENABLE 0x0001 +#define AM335X_I2C_DMATXENABLE_CLR 0x44 +#define I2C_DMATXENABLE 0x0001 +#define AM335X_I2C_DMARXWAKE_EN 0x48 + /* use same bits as IRQ */ +#define AM335X_I2C_DMATXWAKE_EN 0x4C + /* use same bits as IRQ */ +#define AM335X_I2C_SYSS 0x90 +#define I2C_SYSS_RDONE 0x0001 +#define AM335X_I2C_BUF 0x94 +#define I2C_BUF_RDMA_EN 0x8000 +#define I2C_BUF_RXFIFO_CLR 0x4000 +#define I2C_BUF_RXTRSH_MASK 0x3f00 +#define I2C_BUF_RXTRSH(x) ((x) << 8) +#define I2C_BUF_XDMA_EN 0x0080 +#define I2C_BUF_TXFIFO_CLR 0x0040 +#define I2C_BUF_TXTRSH_MASK 0x003f +#define I2C_BUF_TXTRSH(x) ((x) << 0) +#define AM335X_I2C_CNT 0x98 +#define I2C_CNT_MASK 0xffff +#define AM335X_I2C_DATA 0x9C +#define I2C_DATA_MASK 0x00ff +#define AM335X_I2C_CON 0xA4 +#define I2C_CON_EN 0x8000 +#define I2C_CON_STB 0x0800 +#define I2C_CON_MST 0x0400 +#define I2C_CON_TRX 0x0200 +#define I2C_CON_XSA 0x0100 +#define I2C_CON_XOA0 0x0080 +#define I2C_CON_XOA1 0x0040 +#define I2C_CON_XOA2 0x0020 +#define I2C_CON_XOA3 0x0010 +#define I2C_CON_STP 0x0002 +#define I2C_CON_STT 0x0001 +#define AM335X_I2C_OA 0xA8 +#define I2C_OA_MASK 0x03ff +#define AM335X_I2C_SA 0xAC +#define I2C_SA_MASK 0x03ff +#define AM335X_I2C_PSC 0xB0 +#define I2C_PSC_MASK 0x000f +#define AM335X_I2C_SCLL 0xB4 +#define I2C_SCLL_MASK 0x000f +#define AM335X_I2C_SCLH 0xB8 +#define I2C_SCLH_MASK 0x000f +#define AM335X_I2C_SYSTEST 0xBC +#define AM335X_I2C_BUFSTAT 0xC0 +#define I2C_BUFSTAT_FIFODEPTH(x) (((x) >> 14) & 0x03) +#define I2C_BUFSTAT_RXSTAT(x) (((x) >> 8) & 0x3f) +#define I2C_BUFSTAT_TXSTAT(x) (((x) >> 0) & 0x3f) +#define AM335X_I2C_OA1 0xC4 +#define AM335X_I2C_OA2 0xC8 +#define AM335X_I2C_OA3 0xCC + /* same bits as I2C_OA */ +#define AM335X_I2C_ACTOA 0xD0 +#define AM335X_I2C_SBLOCK 0xD4 +#define I2C_ACTOA_OA3_ACT 0x0008 +#define I2C_ACTOA_OA2_ACT 0x0004 +#define I2C_ACTOA_OA1_ACT 0x0002 +#define I2C_ACTOA_OA0_ACT 0x0001 + +#endif /* _AM335XIICREG_H */ -- 2.41.0