From: kremlin Date: Wed, 4 May 2016 04:49:38 +0000 (-0400) Subject: initial commit, pull in sys/arch/armv7/omap X-Git-Url: https://uglyman.kremlin.cc/gitweb/gitweb.cgi?p=bbb-pru.git;a=commitdiff_plain initial commit, pull in sys/arch/armv7/omap --- cf3c20aec4364be4014452cd0299590e4160e822 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 */