Index: head/sys/arm/ti/omap3/omap3_reg.h =================================================================== --- head/sys/arm/ti/omap3/omap3_reg.h (revision 273040) +++ head/sys/arm/ti/omap3/omap3_reg.h (nonexistent) @@ -1,780 +0,0 @@ -/*- - * 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. - * - * $FreeBSD$ - */ - -/* - * Texas Instruments - OMAP3xxx series processors - * - * Reference: - * OMAP35x Applications Processor - * Technical Reference Manual - * (omap35xx_techref.pdf) - * - * - * Note: - * The devices are mapped into address above 0xD000_0000 as the kernel space - * memory is at 0xC000_0000 and above. The first 256MB after this is reserved - * for the size of the kernel, everything above that is reserved for SoC - * devices. - * - */ -#ifndef _OMAP35XX_REG_H_ -#define _OMAP35XX_REG_H_ - -#ifndef _LOCORE -#include /* for uint32_t */ -#endif - - - - -#define OMAP35XX_SDRAM0_START 0x80000000UL -#define OMAP35XX_SDRAM1_START 0xA0000000UL -#define OMAP35XX_SDRAM_BANKS 2 -#define OMAP35XX_SDRAM_BANK_SIZE 0x20000000UL - - -/* Physical/Virtual address for SDRAM controller */ - -#define OMAP35XX_SMS_VBASE 0x6C000000UL -#define OMAP35XX_SMS_HWBASE 0x6C000000UL -#define OMAP35XX_SMS_SIZE 0x01000000UL - -#define OMAP35XX_SDRC_VBASE 0x6D000000UL -#define OMAP35XX_SDRC_HWBASE 0x6D000000UL -#define OMAP35XX_SDRC_SIZE 0x01000000UL - - - -/* Physical/Virtual address for I/O space */ - -#define OMAP35XX_L3_VBASE 0xD0000000UL -#define OMAP35XX_L3_HWBASE 0x68000000UL -#define OMAP35XX_L3_SIZE 0x01000000UL - -#define OMAP35XX_L4_CORE_VBASE 0xE8000000UL -#define OMAP35XX_L4_CORE_HWBASE 0x48000000UL -#define OMAP35XX_L4_CORE_SIZE 0x01000000UL - -#define OMAP35XX_L4_WAKEUP_VBASE 0xE8300000UL -#define OMAP35XX_L4_WAKEUP_HWBASE 0x48300000UL -#define OMAP35XX_L4_WAKEUP_SIZE 0x00040000UL - -#define OMAP35XX_L4_PERIPH_VBASE 0xE9000000UL -#define OMAP35XX_L4_PERIPH_HWBASE 0x49000000UL -#define OMAP35XX_L4_PERIPH_SIZE 0x00100000UL - - -/* - * L4-CORE Physical/Virtual addresss offsets - */ -#define OMAP35XX_SCM_OFFSET 0x00002000UL -#define OMAP35XX_CM_OFFSET 0x00004000UL -#define OMAP35XX_SDMA_OFFSET 0x00056000UL -#define OMAP35XX_I2C3_OFFSET 0x00060000UL -#define OMAP35XX_USB_TLL_OFFSET 0x00062000UL -#define OMAP35XX_USB_UHH_OFFSET 0x00064000UL -#define OMAP35XX_USB_EHCI_OFFSET 0x00064800UL - - -#define OMAP35XX_UART1_OFFSET 0x0006A000UL -#define OMAP35XX_UART2_OFFSET 0x0006C000UL -#define OMAP35XX_I2C1_OFFSET 0x00070000UL -#define OMAP35XX_I2C2_OFFSET 0x00072000UL -#define OMAP35XX_MCBSP1_OFFSET 0x00074000UL -#define OMAP35XX_GPTIMER10_OFFSET 0x00086000UL -#define OMAP35XX_GPTIMER11_OFFSET 0x00088000UL -#define OMAP35XX_MCBSP5_OFFSET 0x00096000UL -#define OMAP35XX_MMU1_OFFSET 0x000BD400UL -#define OMAP35XX_INTCPS_OFFSET 0x00200000UL - - -/* - * L4-WAKEUP Physical/Virtual addresss offsets - */ -#define OMAP35XX_PRM_OFFSET 0x00006000UL -#define OMAP35XX_GPIO1_OFFSET 0x00010000UL -#define OMAP35XX_GPTIMER1_OFFSET 0x00018000UL - - - -/* - * L4-PERIPH Physical/Virtual addresss offsets - */ -#define OMAP35XX_UART3_OFFSET 0x00020000UL -#define OMAP35XX_MCBSP2_OFFSET 0x00022000UL -#define OMAP35XX_MCBSP3_OFFSET 0x00024000UL -#define OMAP35XX_MCBSP4_OFFSET 0x00026000UL -#define OMAP35XX_SIDETONE_MCBSP2_OFFSET 0x00028000UL -#define OMAP35XX_SIDETONE_MCBSP3_OFFSET 0x0002A000UL -#define OMAP35XX_GPTIMER2_OFFSET 0x00032000UL -#define OMAP35XX_GPTIMER3_OFFSET 0x00034000UL -#define OMAP35XX_GPTIMER4_OFFSET 0x00036000UL -#define OMAP35XX_GPTIMER5_OFFSET 0x00038000UL -#define OMAP35XX_GPTIMER6_OFFSET 0x0003A000UL -#define OMAP35XX_GPTIMER7_OFFSET 0x0003C000UL -#define OMAP35XX_GPTIMER8_OFFSET 0x0003E000UL -#define OMAP35XX_GPTIMER9_OFFSET 0x00040000UL -#define OMAP35XX_GPIO2_OFFSET 0x00050000UL -#define OMAP35XX_GPIO3_OFFSET 0x00052000UL -#define OMAP35XX_GPIO4_OFFSET 0x00054000UL -#define OMAP35XX_GPIO5_OFFSET 0x00056000UL -#define OMAP35XX_GPIO6_OFFSET 0x00058000UL - - - - - - -/* - * System Control Module - */ -#define OMAP35XX_SCM_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_SCM_OFFSET) -#define OMAP35XX_SCM_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_SCM_OFFSET) -#define OMAP35XX_SCM_SIZE 0x00001000UL - -#define OMAP35XX_SCM_REVISION 0x00000000UL -#define OMAP35XX_SCM_SYSCONFIG 0x00000010UL -#define OMAP35XX_SCM_PADCONFS_BASE 0x00000030UL -#define OMAP35XX_SCM_DEVCONF0 0x00000274UL -#define OMAP35XX_SCM_MEM_DFTRW0 0x00000278UL - - - - -/* - * - */ -#define OMAP35XX_CM_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_CM_OFFSET) -#define OMAP35XX_CM_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_CM_OFFSET) -#define OMAP35XX_CM_SIZE 0x00001500UL - -#define OMAP35XX_CM_CORE_OFFSET 0x00000A00UL -#define OMAP35XX_CM_CORE_SIZE 0x00000100UL -#define OMAP35XX_CM_FCLKEN1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0000UL) -#define OMAP35XX_CM_FCLKEN3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0008UL) -#define OMAP35XX_CM_ICLKEN1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0010UL) -#define OMAP35XX_CM_ICLKEN2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0014UL) -#define OMAP35XX_CM_ICLKEN3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0018UL) -#define OMAP35XX_CM_IDLEST1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0020UL) -#define OMAP35XX_CM_IDLEST2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0024UL) -#define OMAP35XX_CM_IDLEST3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0028UL) -#define OMAP35XX_CM_AUTOIDLE1_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0030UL) -#define OMAP35XX_CM_AUTOIDLE2_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0034UL) -#define OMAP35XX_CM_AUTOIDLE3_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0038UL) -#define OMAP35XX_CM_CLKSEL_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0040UL) -#define OMAP35XX_CM_CLKSTCTRL_CORE (OMAP35XX_CM_CORE_OFFSET + 0x0048UL) -#define OMAP35XX_CM_CLKSTST_CORE (OMAP35XX_CM_CORE_OFFSET + 0x004CUL) - -#define OMAP35XX_CM_WKUP_OFFSET 0x00000C00UL -#define OMAP35XX_CM_WKUP_SIZE 0x00000100UL -#define OMAP35XX_CM_FCLKEN_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0000UL) -#define OMAP35XX_CM_ICLKEN_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0010UL) -#define OMAP35XX_CM_IDLEST_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0020UL) -#define OMAP35XX_CM_AUTOIDLE_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0030UL) -#define OMAP35XX_CM_CLKSEL_WKUP (OMAP35XX_CM_WKUP_OFFSET + 0x0040UL) - -#define OMAP35XX_CM_PLL_OFFSET 0x00000D00UL -#define OMAP35XX_CM_PLL_SIZE 0x00000100UL -#define OMAP35XX_CM_CLKEN_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0000UL) -#define OMAP35XX_CM_CLKEN2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0004UL) -#define OMAP35XX_CM_IDLEST_CKGEN (OMAP35XX_CM_PLL_OFFSET + 0x0020UL) -#define OMAP35XX_CM_IDLEST2_CKGEN (OMAP35XX_CM_PLL_OFFSET + 0x0024UL) -#define OMAP35XX_CM_AUTOIDLE_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0030UL) -#define OMAP35XX_CM_AUTOIDLE2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0034UL) -#define OMAP35XX_CM_CLKSEL1_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0040UL) -#define OMAP35XX_CM_CLKSEL2_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0044UL) -#define OMAP35XX_CM_CLKSEL3_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0048UL) -#define OMAP35XX_CM_CLKSEL4_PLL (OMAP35XX_CM_PLL_OFFSET + 0x004CUL) -#define OMAP35XX_CM_CLKSEL5_PLL (OMAP35XX_CM_PLL_OFFSET + 0x0050UL) -#define OMAP35XX_CM_CLKOUT_CTRL (OMAP35XX_CM_PLL_OFFSET + 0x0070UL) - -#define OMAP35XX_CM_PER_OFFSET 0x00001000UL -#define OMAP35XX_CM_PER_SIZE 0x00000100UL -#define OMAP35XX_CM_FCLKEN_PER (OMAP35XX_CM_PER_OFFSET + 0x0000UL) -#define OMAP35XX_CM_ICLKEN_PER (OMAP35XX_CM_PER_OFFSET + 0x0010UL) -#define OMAP35XX_CM_IDLEST_PER (OMAP35XX_CM_PER_OFFSET + 0x0020UL) -#define OMAP35XX_CM_AUTOIDLE_PER (OMAP35XX_CM_PER_OFFSET + 0x0030UL) -#define OMAP35XX_CM_CLKSEL_PER (OMAP35XX_CM_PER_OFFSET + 0x0040UL) -#define OMAP35XX_CM_SLEEPDEP_PER (OMAP35XX_CM_PER_OFFSET + 0x0044UL) -#define OMAP35XX_CM_CLKSTCTRL_PER (OMAP35XX_CM_PER_OFFSET + 0x0048UL) -#define OMAP35XX_CM_CLKSTST_PER (OMAP35XX_CM_PER_OFFSET + 0x004CUL) - -#define OMAP35XX_CM_USBHOST_OFFSET 0x00001400UL -#define OMAP35XX_CM_USBHOST_SIZE 0x00000100UL -#define OMAP35XX_CM_FCLKEN_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0000UL) -#define OMAP35XX_CM_ICLKEN_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0010UL) -#define OMAP35XX_CM_IDLEST_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0020UL) -#define OMAP35XX_CM_AUTOIDLE_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0030UL) -#define OMAP35XX_CM_SLEEPDEP_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0044UL) -#define OMAP35XX_CM_CLKSTCTRL_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x0048UL) -#define OMAP35XX_CM_CLKSTST_USBHOST (OMAP35XX_CM_USBHOST_OFFSET + 0x004CUL) - - - - -/* - * - */ -#define OMAP35XX_PRM_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_PRM_OFFSET) -#define OMAP35XX_PRM_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_PRM_OFFSET) -#define OMAP35XX_PRM_SIZE 0x00001600UL - -#define OMAP35XX_PRM_CLKCTRL_OFFSET 0x00000D00UL -#define OMAP35XX_PRM_CLKCTRL_SIZE 0x00000100UL -#define OMAP35XX_PRM_CLKSEL (OMAP35XX_PRM_CLKCTRL_OFFSET + 0x0040UL) -#define OMAP35XX_PRM_CLKOUT_CTRL (OMAP35XX_PRM_CLKCTRL_OFFSET + 0x0070UL) - -#define OMAP35XX_PRM_GLOBAL_OFFSET 0x00001200UL -#define OMAP35XX_PRM_GLOBAL_SIZE 0x00000100UL -#define OMAP35XX_PRM_CLKSRC_CTRL (OMAP35XX_PRM_GLOBAL_OFFSET + 0x0070UL) - - - - - -/* - * Uarts - */ -#define OMAP35XX_UART1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_UART1_OFFSET) -#define OMAP35XX_UART1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_UART1_OFFSET) -#define OMAP35XX_UART1_SIZE 0x00001000UL - -#define OMAP35XX_UART2_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_UART2_OFFSET) -#define OMAP35XX_UART2_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_UART2_OFFSET) -#define OMAP35XX_UART2_SIZE 0x00001000UL - -#define OMAP35XX_UART3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_UART3_OFFSET) -#define OMAP35XX_UART3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_UART3_OFFSET) -#define OMAP35XX_UART3_SIZE 0x00001000UL - - - - -/* - * I2C Modules - */ -#define OMAP35XX_I2C1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C1_OFFSET) -#define OMAP35XX_I2C1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C1_OFFSET) -#define OMAP35XX_I2C1_SIZE 0x00000080UL - -#define OMAP35XX_I2C2_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C2_OFFSET) -#define OMAP35XX_I2C2_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C2_OFFSET) -#define OMAP35XX_I2C2_SIZE 0x00000080UL - -#define OMAP35XX_I2C3_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_I2C3_OFFSET) -#define OMAP35XX_I2C3_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_I2C3_OFFSET) -#define OMAP35XX_I2C3_SIZE 0x00000080UL - -#define OMAP35XX_I2C_IE 0x04 -#define OMAP35XX_I2C_STAT 0x08 -#define OMAP35XX_I2C_WE 0x0C -#define OMAP35XX_I2C_SYSS 0x10 -#define OMAP35XX_I2C_BUF 0x14 -#define OMAP35XX_I2C_CNT 0x18 -#define OMAP35XX_I2C_DATA 0x1C -#define OMAP35XX_I2C_SYSC 0x20 -#define OMAP35XX_I2C_CON 0x24 -#define OMAP35XX_I2C_OA0 0x28 -#define OMAP35XX_I2C_SA 0x2C -#define OMAP35XX_I2C_PSC 0x30 -#define OMAP35XX_I2C_SCLL 0x34 -#define OMAP35XX_I2C_SCLH 0x38 -#define OMAP35XX_I2C_SYSTEST 0x3C -#define OMAP35XX_I2C_BUFSTAT 0x40 -#define OMAP35XX_I2C_OA1 0x44 -#define OMAP35XX_I2C_OA2 0x48 -#define OMAP35XX_I2C_OA3 0x4C -#define OMAP35XX_I2C_ACTOA 0x50 -#define OMAP35XX_I2C_SBLOCK 0x54 - - - -/* - * McBSP Modules - */ -#define OMAP35XX_MCBSP1_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_MCBSP1_OFFSET) -#define OMAP35XX_MCBSP1_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_MCBSP1_OFFSET) -#define OMAP35XX_MCBSP1_SIZE 0x00001000UL - -#define OMAP35XX_MCBSP2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP2_OFFSET) -#define OMAP35XX_MCBSP2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP2_OFFSET) -#define OMAP35XX_MCBSP2_SIZE 0x00001000UL - -#define OMAP35XX_MCBSP3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP3_OFFSET) -#define OMAP35XX_MCBSP3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP3_OFFSET) -#define OMAP35XX_MCBSP3_SIZE 0x00001000UL - -#define OMAP35XX_MCBSP4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_MCBSP4_OFFSET) -#define OMAP35XX_MCBSP4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_MCBSP4_OFFSET) -#define OMAP35XX_MCBSP4_SIZE 0x00001000UL - -#define OMAP35XX_MCBSP5_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_MCBSP5_OFFSET) -#define OMAP35XX_MCBSP5_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_MCBSP5_OFFSET) -#define OMAP35XX_MCBSP5_SIZE 0x00001000UL - -#define OMAP35XX_MCBSP_DRR 0x0000 -#define OMAP35XX_MCBSP_DXR 0x0008 -#define OMAP35XX_MCBSP_SPCR2 0x0010 -#define OMAP35XX_MCBSP_SPCR1 0x0014 -#define OMAP35XX_MCBSP_RCR2 0x0018 -#define OMAP35XX_MCBSP_RCR1 0x001C -#define OMAP35XX_MCBSP_XCR2 0x0020 -#define OMAP35XX_MCBSP_XCR1 0x0024 -#define OMAP35XX_MCBSP_SRGR2 0x0028 -#define OMAP35XX_MCBSP_SRGR1 0x002C -#define OMAP35XX_MCBSP_MCR2 0x0030 -#define OMAP35XX_MCBSP_MCR1 0x0034 -#define OMAP35XX_MCBSP_RCERA 0x0038 -#define OMAP35XX_MCBSP_RCERB 0x003C -#define OMAP35XX_MCBSP_XCERA 0x0040 -#define OMAP35XX_MCBSP_XCERB 0x0044 -#define OMAP35XX_MCBSP_PCR 0x0048 -#define OMAP35XX_MCBSP_RCERC 0x004C -#define OMAP35XX_MCBSP_RCERD 0x0050 -#define OMAP35XX_MCBSP_XCERC 0x0054 -#define OMAP35XX_MCBSP_XCERD 0x0058 -#define OMAP35XX_MCBSP_RCERE 0x005C -#define OMAP35XX_MCBSP_RCERF 0x0060 -#define OMAP35XX_MCBSP_XCERE 0x0064 -#define OMAP35XX_MCBSP_XCERF 0x0068 -#define OMAP35XX_MCBSP_RCERG 0x006C -#define OMAP35XX_MCBSP_RCERH 0x0070 -#define OMAP35XX_MCBSP_XCERG 0x0074 -#define OMAP35XX_MCBSP_XCERH 0x0078 -#define OMAP35XX_MCBSP_RINTCLR 0x0080 -#define OMAP35XX_MCBSP_XINTCLR 0x0084 -#define OMAP35XX_MCBSP_ROVFLCLR 0x0088 -#define OMAP35XX_MCBSP_SYSCONFIG 0x008C -#define OMAP35XX_MCBSP_THRSH2 0x0090 -#define OMAP35XX_MCBSP_THRSH1 0x0094 -#define OMAP35XX_MCBSP_IRQSTATUS 0x00A0 -#define OMAP35XX_MCBSP_IRQENABLE 0x00A4 -#define OMAP35XX_MCBSP_WAKEUPEN 0x00A8 -#define OMAP35XX_MCBSP_XCCR 0x00AC -#define OMAP35XX_MCBSP_RCCR 0x00B0 -#define OMAP35XX_MCBSP_XBUFFSTAT 0x00B4 -#define OMAP35XX_MCBSP_RBUFFSTAT 0x00B8 -#define OMAP35XX_MCBSP_SSELCR 0x00BC -#define OMAP35XX_MCBSP_STATUS 0x00C0 - - - -/* - * USB TTL Module - */ -#define OMAP35XX_USBTLL_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USBTLL_OFFSET) -#define OMAP35XX_USBTLL_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USBTLL_OFFSET) -#define OMAP35XX_USBTLL_SIZE 0x00001000UL - -#define OMAP35XX_USBTLL_REVISION 0x0000 -#define OMAP35XX_USBTLL_SYSCONFIG 0x0010 -#define OMAP35XX_USBTLL_SYSSTATUS 0x0014 -#define OMAP35XX_USBTLL_IRQSTATUS 0x0018 -#define OMAP35XX_USBTLL_IRQENABLE 0x001C -#define OMAP35XX_USBTLL_TLL_SHARED_CONF 0x0030 -#define OMAP35XX_USBTLL_TLL_CHANNEL_CONF(i) (0x0040 + (0x04 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_ID_LO(i) (0x0800 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_ID_HI(i) (0x0801 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_PRODUCT_ID_LO(i) (0x0802 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_PRODUCT_ID_HI(i) (0x0803 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL(i) (0x0804 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL_SET(i) (0x0805 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_FUNCTION_CTRL_CLR(i) (0x0806 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL(i) (0x0807 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL_SET(i) (0x0808 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_INTERFACE_CTRL_CLR(i) (0x0809 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_OTG_CTRL(i) (0x080A + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_OTG_CTRL_SET(i) (0x080B + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_OTG_CTRL_CLR(i) (0x080C + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE(i) (0x080D + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE_SET(i) (0x080E + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_RISE_CLR(i) (0x080F + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL(i) (0x0810 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL_SET(i) (0x0811 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_EN_FALL_CLR(i) (0x0812 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_STATUS(i) (0x0813 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_LATCH(i) (0x0814 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_DEBUG(i) (0x0815 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER(i) (0x0816 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER_SET(i) (0x0817 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_SCRATCH_REGISTER_CLR(i) (0x0818 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_EXTENDED_SET_ACCESS(i) (0x082F + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN(i) (0x0830 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN_SET(i) (0x0831 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_EN_CLR(i) (0x0832 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_STATUS(i) (0x0833 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VCONTROL_LATCH(i) (0x0834 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS(i) (0x0835 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS_SET(i) (0x0836 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_UTMI_VSTATUS_CLR(i) (0x0837 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_USB_INT_LATCH_NOCLR(i) (0x0838 + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN(i) (0x083B + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN_SET(i) (0x083C + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_EN_CLR(i) (0x083D + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_STATUS(i) (0x083E + (0x100 * (i))) -#define OMAP35XX_USBTLL_ULPI_VENDOR_INT_LATCH(i) (0x083F + (0x100 * (i))) - - -/* - * USB Host Module - */ -#define OMAP35XX_USB_TLL_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_TLL_OFFSET) -#define OMAP35XX_USB_TLL_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_TLL_OFFSET) -#define OMAP35XX_USB_TLL_SIZE 0x00001000UL - -#define OMAP35XX_USB_EHCI_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_EHCI_OFFSET) -#define OMAP35XX_USB_EHCI_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_EHCI_OFFSET) -#define OMAP35XX_USB_EHCI_SIZE 0x00000400UL - -#define OMAP35XX_USB_UHH_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_USB_UHH_OFFSET) -#define OMAP35XX_USB_UHH_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_USB_UHH_OFFSET) -#define OMAP35XX_USB_UHH_SIZE 0x00000400UL - - - - - -/* - * SDRAM Controler (SDRC) - * PA 0x6D00_0000 - */ - -#define OMAP35XX_SDRC_SYSCONFIG (OMAP35XX_SDRC_VBASE + 0x10) -#define OMAP35XX_SDRC_SYSSTATUS (OMAP35XX_SDRC_VBASE + 0x14) -#define OMAP35XX_SDRC_CS_CFG (OMAP35XX_SDRC_VBASE + 0x40) -#define OMAP35XX_SDRC_SHARING (OMAP35XX_SDRC_VBASE + 0x44) -#define OMAP35XX_SDRC_ERR_ADDR (OMAP35XX_SDRC_VBASE + 0x48) -#define OMAP35XX_SDRC_ERR_TYPE (OMAP35XX_SDRC_VBASE + 0x4C) -#define OMAP35XX_SDRC_DLLA_CTRL (OMAP35XX_SDRC_VBASE + 0x60) -#define OMAP35XX_SDRC_DLLA_STATUS (OMAP35XX_SDRC_VBASE + 0x64) -#define OMAP35XX_SDRC_POWER_REG (OMAP35XX_SDRC_VBASE + 0x70) -#define OMAP35XX_SDRC_MCFG(p) (OMAP35XX_SDRC_VBASE + 0x80 + (0x30 * (p))) -#define OMAP35XX_SDRC_MR(p) (OMAP35XX_SDRC_VBASE + 0x84 + (0x30 * (p))) -#define OMAP35XX_SDRC_EMR2(p) (OMAP35XX_SDRC_VBASE + 0x8C + (0x30 * (p))) -#define OMAP35XX_SDRC_ACTIM_CTRLA(p) (OMAP35XX_SDRC_VBASE + 0x9C + (0x28 * (p))) -#define OMAP35XX_SDRC_ACTIM_CTRLB(p) (OMAP35XX_SDRC_VBASE + 0xA0 + (0x28 * (p))) -#define OMAP35XX_SDRC_RFR_CTRL(p) (OMAP35XX_SDRC_VBASE + 0xA4 + (0x30 * (p))) -#define OMAP35XX_SDRC_MANUAL(p) (OMAP35XX_SDRC_VBASE + 0xA8 + (0x30 * (p))) - - -/* - * SDMA Offset - * PA 0x4805 6000 - */ - -#define OMAP35XX_SDMA_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_SDMA_OFFSET) -#define OMAP35XX_SDMA_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_SDMA_OFFSET) -#define OMAP35XX_SDMA_SIZE 0x00001000UL - - - -/* - * Interrupt Controller Unit. - * PA 0x4820_0000 - */ - -#define OMAP35XX_INTCPS_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_INTCPS_OFFSET) -#define OMAP35XX_INTCPS_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_INTCPS_OFFSET) -#define OMAP35XX_INTCPS_SIZE 0x00001000UL - -#define OMAP35XX_INTCPS_SYSCONFIG (OMAP35XX_INTCPS_VBASE + 0x10) -#define OMAP35XX_INTCPS_SYSSTATUS (OMAP35XX_INTCPS_VBASE + 0x14) -#define OMAP35XX_INTCPS_SIR_IRQ (OMAP35XX_INTCPS_VBASE + 0x40) -#define OMAP35XX_INTCPS_SIR_FIQ (OMAP35XX_INTCPS_VBASE + 0x44) -#define OMAP35XX_INTCPS_CONTROL (OMAP35XX_INTCPS_VBASE + 0x48) -#define OMAP35XX_INTCPS_PROTECTION (OMAP35XX_INTCPS_VBASE + 0x4C) -#define OMAP35XX_INTCPS_IDLE (OMAP35XX_INTCPS_VBASE + 0x50) -#define OMAP35XX_INTCPS_IRQ_PRIORITY (OMAP35XX_INTCPS_VBASE + 0x60) -#define OMAP35XX_INTCPS_FIQ_PRIORITY (OMAP35XX_INTCPS_VBASE + 0x64) -#define OMAP35XX_INTCPS_THRESHOLD (OMAP35XX_INTCPS_VBASE + 0x68) -#define OMAP35XX_INTCPS_ITR(n) (OMAP35XX_INTCPS_VBASE + 0x80 + (0x20 * (n))) -#define OMAP35XX_INTCPS_MIR(n) (OMAP35XX_INTCPS_VBASE + 0x84 + (0x20 * (n))) -#define OMAP35XX_INTCPS_MIR_CLEAR(n) (OMAP35XX_INTCPS_VBASE + 0x88 + (0x20 * (n))) -#define OMAP35XX_INTCPS_MIR_SET(n) (OMAP35XX_INTCPS_VBASE + 0x8C + (0x20 * (n))) -#define OMAP35XX_INTCPS_ISR_SET(n) (OMAP35XX_INTCPS_VBASE + 0x90 + (0x20 * (n))) -#define OMAP35XX_INTCPS_ISR_CLEAR(n) (OMAP35XX_INTCPS_VBASE + 0x94 + (0x20 * (n))) -#define OMAP35XX_INTCPS_PENDING_IRQ(n) (OMAP35XX_INTCPS_VBASE + 0x98 + (0x20 * (n))) -#define OMAP35XX_INTCPS_PENDING_FIQ(n) (OMAP35XX_INTCPS_VBASE + 0x9C + (0x20 * (n))) -#define OMAP35XX_INTCPS_ILR(m) (OMAP35XX_INTCPS_VBASE + 0x100 + (0x4 * (m))) - - -#define OMAP35XX_IRQ_EMUINT 0 /* MPU emulation(2) */ -#define OMAP35XX_IRQ_COMMTX 1 /* MPU emulation(2) */ -#define OMAP35XX_IRQ_COMMRX 2 /* MPU emulation(2) */ -#define OMAP35XX_IRQ_BENCH 3 /* MPU emulation(2) */ -#define OMAP35XX_IRQ_MCBSP2_ST 4 /* Sidetone MCBSP2 overflow */ -#define OMAP35XX_IRQ_MCBSP3_ST 5 /* Sidetone MCBSP3 overflow */ -#define OMAP35XX_IRQ_SSM_ABORT 6 /* MPU subsystem secure state-machine abort (2) */ -#define OMAP35XX_IRQ_SYS_NIRQ 7 /* External source (active low) */ -#define OMAP35XX_IRQ_RESERVED8 8 /* RESERVED */ -#define OMAP35XX_IRQ_SMX_DBG 9 /* SMX error for debug */ -#define OMAP35XX_IRQ_SMX_APP 10 /* SMX error for application */ -#define OMAP35XX_IRQ_PRCM_MPU 11 /* PRCM module IRQ */ -#define OMAP35XX_IRQ_SDMA0 12 /* System DMA request 0(3) */ -#define OMAP35XX_IRQ_SDMA1 13 /* System DMA request 1(3) */ -#define OMAP35XX_IRQ_SDMA2 14 /* System DMA request 2 */ -#define OMAP35XX_IRQ_SDMA3 15 /* System DMA request 3 */ -#define OMAP35XX_IRQ_MCBSP1 16 /* McBSP module 1 IRQ (3) */ -#define OMAP35XX_IRQ_MCBSP2 17 /* McBSP module 2 IRQ (3) */ -#define OMAP35XX_IRQ_SR1 18 /* SmartReflex™ 1 */ -#define OMAP35XX_IRQ_SR2 19 /* SmartReflex™ 2 */ -#define OMAP35XX_IRQ_GPMC 20 /* General-purpose memory controller module */ -#define OMAP35XX_IRQ_SGX 21 /* 2D/3D graphics module */ -#define OMAP35XX_IRQ_MCBSP3 22 /* McBSP module 3(3) */ -#define OMAP35XX_IRQ_MCBSP4 23 /* McBSP module 4(3) */ -#define OMAP35XX_IRQ_CAM0 24 /* Camera interface request 0 */ -#define OMAP35XX_IRQ_DSS 25 /* Display subsystem module(3) */ -#define OMAP35XX_IRQ_MAIL_U0 26 /* Mailbox user 0 request */ -#define OMAP35XX_IRQ_MCBSP5_IRQ1 27 /* McBSP module 5 (3) */ -#define OMAP35XX_IRQ_IVA2_MMU 28 /* IVA2 MMU */ -#define OMAP35XX_IRQ_GPIO1_MPU 29 /* GPIO module 1(3) */ -#define OMAP35XX_IRQ_GPIO2_MPU 30 /* GPIO module 2(3) */ -#define OMAP35XX_IRQ_GPIO3_MPU 31 /* GPIO module 3(3) */ -#define OMAP35XX_IRQ_GPIO4_MPU 32 /* GPIO module 4(3) */ -#define OMAP35XX_IRQ_GPIO5_MPU 33 /* GPIO module 5(3) */ -#define OMAP35XX_IRQ_GPIO6_MPU 34 /* GPIO module 6(3) */ -#define OMAP35XX_IRQ_USIM 35 /* USIM interrupt (HS devices only) (4) */ -#define OMAP35XX_IRQ_WDT3 36 /* Watchdog timer module 3 overflow */ -#define OMAP35XX_IRQ_GPT1 37 /* General-purpose timer module 1 */ -#define OMAP35XX_IRQ_GPT2 38 /* General-purpose timer module 2 */ -#define OMAP35XX_IRQ_GPT3 39 /* General-purpose timer module 3 */ -#define OMAP35XX_IRQ_GPT4 40 /* General-purpose timer module 4 */ -#define OMAP35XX_IRQ_GPT5 41 /* General-purpose timer module 5(3) */ -#define OMAP35XX_IRQ_GPT6 42 /* General-purpose timer module 6(3) */ -#define OMAP35XX_IRQ_GPT7 43 /* General-purpose timer module 7(3) */ -#define OMAP35XX_IRQ_GPT8 44 /* General-purpose timer module 8(3) */ -#define OMAP35XX_IRQ_GPT9 45 /* General-purpose timer module 9 */ -#define OMAP35XX_IRQ_GPT10 46 /* General-purpose timer module 10 */ -#define OMAP35XX_IRQ_GPT11 47 /* General-purpose timer module 11 */ -#define OMAP35XX_IRQ_SPI4 48 /* McSPI module 4 */ -#define OMAP35XX_IRQ_SHA1MD5_2 49 /* SHA-1/MD5 crypto-accelerator 2 (HS devices only)(4) */ -#define OMAP35XX_IRQ_FPKA_IRQREADY_N 50 /* PKA crypto-accelerator (HS devices only) (4) */ -#define OMAP35XX_IRQ_SHA2MD5 51 /* SHA-2/MD5 crypto-accelerator 1 (HS devices only) (4) */ -#define OMAP35XX_IRQ_RNG 52 /* RNG module (HS devices only) (4) */ -#define OMAP35XX_IRQ_MG 53 /* MG function (3) */ -#define OMAP35XX_IRQ_MCBSP4_TX 54 /* McBSP module 4 transmit(3) */ -#define OMAP35XX_IRQ_MCBSP4_RX 55 /* McBSP module 4 receive(3) */ -#define OMAP35XX_IRQ_I2C1 56 /* I2C module 1 */ -#define OMAP35XX_IRQ_I2C2 57 /* I2C module 2 */ -#define OMAP35XX_IRQ_HDQ 58 /* HDQ / One-wire */ -#define OMAP35XX_IRQ_MCBSP1_TX 59 /* McBSP module 1 transmit(3) */ -#define OMAP35XX_IRQ_MCBSP1_RX 60 /* McBSP module 1 receive(3) */ -#define OMAP35XX_IRQ_I2C3 61 /* I2C module 3 */ -#define OMAP35XX_IRQ_McBSP2_TX 62 /* McBSP module 2 transmit(3) */ -#define OMAP35XX_IRQ_McBSP2_RX 63 /* McBSP module 2 receive(3) */ -#define OMAP35XX_IRQ_FPKA_IRQRERROR_N 64 /* PKA crypto-accelerator (HS devices only) (4) */ -#define OMAP35XX_IRQ_SPI1 65 /* McSPI module 1 */ -#define OMAP35XX_IRQ_SPI2 66 /* McSPI module 2 */ -#define OMAP35XX_IRQ_RESERVED67 67 /* RESERVED */ -#define OMAP35XX_IRQ_RESERVED68 68 /* RESERVED */ -#define OMAP35XX_IRQ_RESERVED69 69 /* RESERVED */ -#define OMAP35XX_IRQ_RESERVED70 70 /* RESERVED */ -#define OMAP35XX_IRQ_RESERVED71 71 /* RESERVED */ -#define OMAP35XX_IRQ_UART1 72 /* UART module 1 */ -#define OMAP35XX_IRQ_UART2 73 /* UART module 2 */ -#define OMAP35XX_IRQ_UART3 74 /* UART module 3 (also infrared)(3) */ -#define OMAP35XX_IRQ_PBIAS 75 /* Merged interrupt for PBIASlite1 and 2 */ -#define OMAP35XX_IRQ_OHCI 76 /* OHCI controller HSUSB MP Host Interrupt */ -#define OMAP35XX_IRQ_EHCI 77 /* EHCI controller HSUSB MP Host Interrupt */ -#define OMAP35XX_IRQ_TLL 78 /* HSUSB MP TLL Interrupt */ -#define OMAP35XX_IRQ_PARTHASH 79 /* SHA2/MD5 crypto-accelerator 1 (HS devices only) (4) */ -#define OMAP35XX_IRQ_RESERVED80 80 /* Reserved */ -#define OMAP35XX_IRQ_MCBSP5_TX 81 /* McBSP module 5 transmit(3) */ -#define OMAP35XX_IRQ_MCBSP5_RX 82 /* McBSP module 5 receive(3) */ -#define OMAP35XX_IRQ_MMC1 83 /* MMC/SD module 1 */ -#define OMAP35XX_IRQ_MS 84 /* MS-PRO™ module */ -#define OMAP35XX_IRQ_RESERVED85 85 /* Reserved */ -#define OMAP35XX_IRQ_MMC2 86 /* MMC/SD module 2 */ -#define OMAP35XX_IRQ_MPU_ICR 87 /* MPU ICR */ -#define OMAP35XX_IRQ_RESERVED 88 /* RESERVED */ -#define OMAP35XX_IRQ_MCBSP3_TX 89 /* McBSP module 3 transmit(3) */ -#define OMAP35XX_IRQ_MCBSP3_RX 90 /* McBSP module 3 receive(3) */ -#define OMAP35XX_IRQ_SPI3 91 /* McSPI module 3 */ -#define OMAP35XX_IRQ_HSUSB_MC_NINT 92 /* High-Speed USB OTG controller */ -#define OMAP35XX_IRQ_HSUSB_DMA_NINT 93 /* High-Speed USB OTG DMA controller */ -#define OMAP35XX_IRQ_MMC3 94 /* MMC/SD module 3 */ -#define OMAP35XX_IRQ_GPT12 95 /* General-purpose timer module 12 */ - - - - -/* - * General Purpose Timers - */ -#define OMAP35XX_GPTIMER1_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_GPTIMER1_OFFSET) -#define OMAP35XX_GPTIMER1_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_GPTIMER1_OFFSET) -#define OMAP35XX_GPTIMER2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER2_OFFSET) -#define OMAP35XX_GPTIMER2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER2_OFFSET) -#define OMAP35XX_GPTIMER3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER3_OFFSET) -#define OMAP35XX_GPTIMER3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER3_OFFSET) -#define OMAP35XX_GPTIMER4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER4_OFFSET) -#define OMAP35XX_GPTIMER4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER4_OFFSET) -#define OMAP35XX_GPTIMER5_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER5_OFFSET) -#define OMAP35XX_GPTIMER5_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER5_OFFSET) -#define OMAP35XX_GPTIMER6_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER6_OFFSET) -#define OMAP35XX_GPTIMER6_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER6_OFFSET) -#define OMAP35XX_GPTIMER7_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER7_OFFSET) -#define OMAP35XX_GPTIMER7_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER7_OFFSET) -#define OMAP35XX_GPTIMER8_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER8_OFFSET) -#define OMAP35XX_GPTIMER8_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER8_OFFSET) -#define OMAP35XX_GPTIMER9_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPTIMER9_OFFSET) -#define OMAP35XX_GPTIMER9_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPTIMER9_OFFSET) -#define OMAP35XX_GPTIMER10_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_GPTIMER10_OFFSET) -#define OMAP35XX_GPTIMER10_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_GPTIMER10_OFFSET) -#define OMAP35XX_GPTIMER11_VBASE (OMAP35XX_L4_CORE_VBASE + OMAP35XX_GPTIMER11_OFFSET) -#define OMAP35XX_GPTIMER11_HWBASE (OMAP35XX_L4_CORE_HWBASE + OMAP35XX_GPTIMER11_OFFSET) -#define OMAP35XX_GPTIMER12_VBASE 0x48304000UL /* GPTIMER12 */ -#define OMAP35XX_GPTIMER_SIZE 0x00001000UL - - - -/* Timer register offsets */ -#define OMAP35XX_GPTIMER_TIOCP_CFG 0x010 -#define OMAP35XX_GPTIMER_TISTAT 0x014 -#define OMAP35XX_GPTIMER_TISR 0x018 -#define OMAP35XX_GPTIMER_TIER 0x01C -#define OMAP35XX_GPTIMER_TWER 0x020 -#define OMAP35XX_GPTIMER_TCLR 0x024 -#define OMAP35XX_GPTIMER_TCRR 0x028 -#define OMAP35XX_GPTIMER_TLDR 0x02C -#define OMAP35XX_GPTIMER_TTGR 0x030 -#define OMAP35XX_GPTIMER_TWPS 0x034 -#define OMAP35XX_GPTIMER_TMAR 0x038 -#define OMAP35XX_GPTIMER_TCAR1 0x03C -#define OMAP35XX_GPTIMER_TSICR 0x040 -#define OMAP35XX_GPTIMER_TCAR2 0x044 -#define OMAP35XX_GPTIMER_TPIR 0x048 -#define OMAP35XX_GPTIMER_TNIR 0x04C -#define OMAP35XX_GPTIMER_TCVR 0x050 -#define OMAP35XX_GPTIMER_TOCR 0x054 -#define OMAP35XX_GPTIMER_TOWR 0x058 - -/* Bit values */ -#define MAT_IT_FLAG 0x01 -#define OVF_IT_FLAG 0x02 -#define TCAR_IT_FLAG 0x04 - - - -/* - * GPIO - General Purpose IO - */ - -/* Base addresses for the GPIO modules */ -#define OMAP35XX_GPIO1_HWBASE (OMAP35XX_L4_WAKEUP_HWBASE + OMAP35XX_GPIO1_OFFSET) -#define OMAP35XX_GPIO1_VBASE (OMAP35XX_L4_WAKEUP_VBASE + OMAP35XX_GPIO1_OFFSET) -#define OMAP35XX_GPIO1_SIZE 0x00001000UL -#define OMAP35XX_GPIO2_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO2_OFFSET) -#define OMAP35XX_GPIO2_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO2_OFFSET) -#define OMAP35XX_GPIO2_SIZE 0x00001000UL -#define OMAP35XX_GPIO3_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO3_OFFSET) -#define OMAP35XX_GPIO3_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO3_OFFSET) -#define OMAP35XX_GPIO3_SIZE 0x00001000UL -#define OMAP35XX_GPIO4_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO4_OFFSET) -#define OMAP35XX_GPIO4_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO4_OFFSET) -#define OMAP35XX_GPIO4_SIZE 0x00001000UL -#define OMAP35XX_GPIO5_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO5_OFFSET) -#define OMAP35XX_GPIO5_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO5_OFFSET) -#define OMAP35XX_GPIO5_SIZE 0x00001000UL -#define OMAP35XX_GPIO6_HWBASE (OMAP35XX_L4_PERIPH_HWBASE + OMAP35XX_GPIO6_OFFSET) -#define OMAP35XX_GPIO6_VBASE (OMAP35XX_L4_PERIPH_VBASE + OMAP35XX_GPIO6_OFFSET) -#define OMAP35XX_GPIO6_SIZE 0x00001000UL - - - -/* Register offsets within the banks above */ -#define OMAP35XX_GPIO_SYSCONFIG 0x010 -#define OMAP35XX_GPIO_SYSSTATUS 0x014 -#define OMAP35XX_GPIO_IRQSTATUS1 0x018 -#define OMAP35XX_GPIO_IRQENABLE1 0x01C -#define OMAP35XX_GPIO_WAKEUPENABLE 0x020 -#define OMAP35XX_GPIO_IRQSTATUS2 0x028 -#define OMAP35XX_GPIO_IRQENABLE2 0x02C -#define OMAP35XX_GPIO_CTRL 0x030 -#define OMAP35XX_GPIO_OE 0x034 -#define OMAP35XX_GPIO_DATAIN 0x038 -#define OMAP35XX_GPIO_DATAOUT 0x03C -#define OMAP35XX_GPIO_LEVELDETECT0 0x040 -#define OMAP35XX_GPIO_LEVELDETECT1 0x044 -#define OMAP35XX_GPIO_RISINGDETECT 0x048 -#define OMAP35XX_GPIO_FALLINGDETECT 0x04C -#define OMAP35XX_GPIO_DEBOUNCENABLE 0x050 -#define OMAP35XX_GPIO_DEBOUNCINGTIME 0x054 -#define OMAP35XX_GPIO_CLEARIRQENABLE1 0x060 -#define OMAP35XX_GPIO_SETIRQENABLE1 0x064 -#define OMAP35XX_GPIO_CLEARIRQENABLE2 0x070 -#define OMAP35XX_GPIO_SETIRQENABLE2 0x074 -#define OMAP35XX_GPIO_CLEARWKUENA 0x080 -#define OMAP35XX_GPIO_SETWKUENA 0x084 -#define OMAP35XX_GPIO_CLEARDATAOUT 0x090 -#define OMAP35XX_GPIO_SETDATAOUT 0x094 - - -/* - * MMC/SD/SDIO - */ - -/* Base addresses for the MMC/SD/SDIO modules */ -#define OMAP35XX_MMCHS1_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x0009C000) -#define OMAP35XX_MMCHS1_VBASE (OMAP35XX_L4_CORE_VBASE + 0x0009C000) -#define OMAP35XX_MMCHS2_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x000B4000) -#define OMAP35XX_MMCHS2_VBASE (OMAP35XX_L4_CORE_VBASE + 0x000B4000) -#define OMAP35XX_MMCHS3_HWBASE (OMAP35XX_L4_CORE_HWBASE + 0x000AD000) -#define OMAP35XX_MMCHS3_VBASE (OMAP35XX_L4_CORE_VBASE + 0x000AD000) -#define OMAP35XX_MMCHS_SIZE 0x00000200UL - -/* Register offsets within each of the MMC/SD/SDIO controllers */ -#define OMAP35XX_MMCHS_SYSCONFIG 0x010 -#define OMAP35XX_MMCHS_SYSSTATUS 0x014 -#define OMAP35XX_MMCHS_CSRE 0x024 -#define OMAP35XX_MMCHS_SYSTEST 0x028 -#define OMAP35XX_MMCHS_CON 0x02C -#define OMAP35XX_MMCHS_PWCNT 0x030 -#define OMAP35XX_MMCHS_BLK 0x104 -#define OMAP35XX_MMCHS_ARG 0x108 -#define OMAP35XX_MMCHS_CMD 0x10C -#define OMAP35XX_MMCHS_RSP10 0x110 -#define OMAP35XX_MMCHS_RSP32 0x114 -#define OMAP35XX_MMCHS_RSP54 0x118 -#define OMAP35XX_MMCHS_RSP76 0x11C -#define OMAP35XX_MMCHS_DATA 0x120 -#define OMAP35XX_MMCHS_PSTATE 0x124 -#define OMAP35XX_MMCHS_HCTL 0x128 -#define OMAP35XX_MMCHS_SYSCTL 0x12C -#define OMAP35XX_MMCHS_STAT 0x130 -#define OMAP35XX_MMCHS_IE 0x134 -#define OMAP35XX_MMCHS_ISE 0x138 -#define OMAP35XX_MMCHS_AC12 0x13C -#define OMAP35XX_MMCHS_CAPA 0x140 -#define OMAP35XX_MMCHS_CUR_CAPA 0x148 -#define OMAP35XX_MMCHS_REV 0x1FC - - - -#endif /* _OMAP35XX_REG_H_ */ Property changes on: head/sys/arm/ti/omap3/omap3_reg.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/sys/arm/ti/ti_cpuid.c =================================================================== --- head/sys/arm/ti/ti_cpuid.c (revision 273040) +++ head/sys/arm/ti/ti_cpuid.c (revision 273041) @@ -1,330 +1,265 @@ /*- * Copyright (c) 2011 * Ben Gray . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #define OMAP4_STD_FUSE_DIE_ID_0 0x2200 #define OMAP4_ID_CODE 0x2204 #define OMAP4_STD_FUSE_DIE_ID_1 0x2208 #define OMAP4_STD_FUSE_DIE_ID_2 0x220C #define OMAP4_STD_FUSE_DIE_ID_3 0x2210 #define OMAP4_STD_FUSE_PROD_ID_0 0x2214 #define OMAP4_STD_FUSE_PROD_ID_1 0x2218 #define OMAP3_ID_CODE 0xA204 static uint32_t chip_revision = 0xffffffff; /** * ti_revision - Returns the revision number of the device * * Simply returns an identifier for the revision of the chip we are running * on. * * RETURNS * A 32-bit identifier for the current chip */ uint32_t ti_revision(void) { return chip_revision; } /** * omap4_get_revision - determines omap4 revision * * Reads the registers to determine the revision of the chip we are currently * running on. Stores the information in global variables. * * */ static void omap4_get_revision(void) { uint32_t id_code; uint32_t revision; uint32_t hawkeye; bus_space_handle_t bsh; /* The chip revsion is read from the device identification registers and * the JTAG (?) tap registers, which are located in address 0x4A00_2200 to * 0x4A00_2218. This is part of the L4_CORE memory range and should have * been mapped in by the machdep.c code. * * STD_FUSE_DIE_ID_0 0x4A00 2200 * ID_CODE 0x4A00 2204 (this is the only one we need) * STD_FUSE_DIE_ID_1 0x4A00 2208 * STD_FUSE_DIE_ID_2 0x4A00 220C * STD_FUSE_DIE_ID_3 0x4A00 2210 * STD_FUSE_PROD_ID_0 0x4A00 2214 * STD_FUSE_PROD_ID_1 0x4A00 2218 */ /* FIXME Should we map somewhere else? */ bus_space_map(fdtbus_bs_tag,OMAP44XX_L4_CORE_HWBASE, 0x4000, 0, &bsh); id_code = bus_space_read_4(fdtbus_bs_tag, bsh, OMAP4_ID_CODE); bus_space_unmap(fdtbus_bs_tag, bsh, 0x4000); hawkeye = ((id_code >> 12) & 0xffff); revision = ((id_code >> 28) & 0xf); /* Apparently according to the linux code there were some ES2.0 samples that * have the wrong id code and report themselves as ES1.0 silicon. So used * the ARM cpuid to get the correct revision. */ if (revision == 0) { id_code = cpufunc_id(); revision = (id_code & 0xf) - 1; } switch (hawkeye) { case 0xB852: switch (revision) { case 0: chip_revision = OMAP4430_REV_ES1_0; break; case 1: chip_revision = OMAP4430_REV_ES2_1; break; default: chip_revision = OMAP4430_REV_UNKNOWN; break; } break; case 0xB95C: switch (revision) { case 3: chip_revision = OMAP4430_REV_ES2_1; break; case 4: chip_revision = OMAP4430_REV_ES2_2; break; case 6: chip_revision = OMAP4430_REV_ES2_3; break; default: chip_revision = OMAP4430_REV_UNKNOWN; break; } break; case 0xB94E: switch (revision) { case 0: chip_revision = OMAP4460_REV_ES1_0; break; case 2: chip_revision = OMAP4460_REV_ES1_1; break; default: chip_revision = OMAP4460_REV_UNKNOWN; break; } break; case 0xB975: switch (revision) { case 0: chip_revision = OMAP4470_REV_ES1_0; break; default: chip_revision = OMAP4470_REV_UNKNOWN; break; } break; default: /* Default to the latest revision if we can't determine type */ chip_revision = OMAP_UNKNOWN_DEV; break; } if (chip_revision != OMAP_UNKNOWN_DEV) { printf("Texas Instruments OMAP%04x Processor, Revision ES%u.%u\n", OMAP_REV_DEVICE(chip_revision), OMAP_REV_MAJOR(chip_revision), OMAP_REV_MINOR(chip_revision)); } else { printf("Texas Instruments unknown OMAP chip: %04x, rev %d\n", hawkeye, revision); } } -/** - * omap3_get_revision - determines omap3 revision - * - * Reads the registers to determine the revision of the chip we are currently - * running on. Stores the information in global variables. - * - * WARNING: This function currently only really works for OMAP3530 devices. - * - * - * - */ static void -omap3_get_revision(void) -{ - uint32_t id_code; - uint32_t revision; - uint32_t hawkeye; - bus_space_handle_t bsh; - - /* The chip revsion is read from the device identification registers and - * the JTAG (?) tap registers, which are located in address 0x4A00_2200 to - * 0x4A00_2218. This is part of the L4_CORE memory range and should have - * been mapped in by the machdep.c code. - * - * CONTROL_IDCODE 0x4830 A204 (this is the only one we need) - * - * - */ - bus_space_map(fdtbus_bs_tag, OMAP35XX_L4_WAKEUP_HWBASE, 0x10000, 0, &bsh); - id_code = bus_space_read_4(fdtbus_bs_tag, bsh, OMAP3_ID_CODE); - bus_space_unmap(fdtbus_bs_tag, bsh, 0x10000); - - hawkeye = ((id_code >> 12) & 0xffff); - revision = ((id_code >> 28) & 0xf); - - switch (hawkeye) { - case 0xB6D6: - chip_revision = OMAP3350_REV_ES1_0; - break; - case 0xB7AE: - if (revision == 1) - chip_revision = OMAP3530_REV_ES2_0; - else if (revision == 2) - chip_revision = OMAP3530_REV_ES2_1; - else if (revision == 3) - chip_revision = OMAP3530_REV_ES3_0; - else if (revision == 4) - chip_revision = OMAP3530_REV_ES3_1; - else if (revision == 7) - chip_revision = OMAP3530_REV_ES3_1_2; - break; - default: - /* Default to the latest revision if we can't determine type */ - chip_revision = OMAP3530_REV_ES3_1_2; - break; - } - printf("Texas Instruments OMAP%04x Processor, Revision ES%u.%u\n", - OMAP_REV_DEVICE(chip_revision), OMAP_REV_MAJOR(chip_revision), - OMAP_REV_MINOR(chip_revision)); -} - -static void am335x_get_revision(void) { uint32_t dev_feature; uint8_t cpu_last_char; bus_space_handle_t bsh; bus_space_map(fdtbus_bs_tag, AM335X_CONTROL_BASE, AM335X_CONTROL_SIZE, 0, &bsh); chip_revision = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEVICE_ID); dev_feature = bus_space_read_4(fdtbus_bs_tag, bsh, AM335X_CONTROL_DEV_FEATURE); bus_space_unmap(fdtbus_bs_tag, bsh, AM335X_CONTROL_SIZE); switch (dev_feature) { case 0x00FF0382: cpu_last_char='2'; break; case 0x20FF0382: cpu_last_char='4'; break; case 0x00FF0383: cpu_last_char='6'; break; case 0x00FE0383: cpu_last_char='7'; break; case 0x20FF0383: cpu_last_char='8'; break; case 0x20FE0383: cpu_last_char='9'; break; default: cpu_last_char='x'; } printf("Texas Instruments AM335%c Processor, Revision ES1.%u\n", cpu_last_char, AM335X_DEVREV(chip_revision)); } /** * ti_cpu_ident - attempts to identify the chip we are running on * @dummy: ignored * * This function is called before any of the driver are initialised, however * the basic virt to phys maps have been setup in machdep.c so we can still * access the required registers, we just have to use direct register reads * and writes rather than going through the bus stuff. * * */ static void ti_cpu_ident(void *dummy) { switch(ti_chip()) { - case CHIP_OMAP_3: - omap3_get_revision(); - break; case CHIP_OMAP_4: omap4_get_revision(); break; case CHIP_AM335X: am335x_get_revision(); break; default: panic("Unknown chip type, fixme!\n"); } } SYSINIT(ti_cpu_ident, SI_SUB_CPU, SI_ORDER_SECOND, ti_cpu_ident, NULL); Index: head/sys/arm/ti/ti_cpuid.h =================================================================== --- head/sys/arm/ti/ti_cpuid.h (revision 273040) +++ head/sys/arm/ti/ti_cpuid.h (revision 273041) @@ -1,89 +1,86 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _TI_CPUID_H_ #define _TI_CPUID_H_ #define OMAP_MAKEREV(d, a, b, c) \ (uint32_t)(((d) << 16) | (((a) & 0xf) << 8) | (((b) & 0xf) << 4) | ((c) & 0xf)) #define OMAP_REV_DEVICE(x) (((x) >> 16) & 0xffff) #define OMAP_REV_MAJOR(x) (((x) >> 8) & 0xf) #define OMAP_REV_MINOR(x) (((x) >> 4) & 0xf) #define OMAP_REV_MINOR_MINOR(x) (((x) >> 0) & 0xf) #define OMAP3350_DEV 0x3530 #define OMAP3350_REV_ES1_0 OMAP_MAKEREV(OMAP3350_DEV, 1, 0, 0) #define OMAP3530_REV_ES2_0 OMAP_MAKEREV(OMAP3350_DEV, 2, 0, 0) #define OMAP3530_REV_ES2_1 OMAP_MAKEREV(OMAP3350_DEV, 2, 1, 0) #define OMAP3530_REV_ES3_0 OMAP_MAKEREV(OMAP3350_DEV, 3, 0, 0) #define OMAP3530_REV_ES3_1 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 0) #define OMAP3530_REV_ES3_1_2 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 2) #define OMAP4430_DEV 0x4430 #define OMAP4430_REV_ES1_0 OMAP_MAKEREV(OMAP4430_DEV, 1, 0, 0) #define OMAP4430_REV_ES2_0 OMAP_MAKEREV(OMAP4430_DEV, 2, 0, 0) #define OMAP4430_REV_ES2_1 OMAP_MAKEREV(OMAP4430_DEV, 2, 1, 0) #define OMAP4430_REV_ES2_2 OMAP_MAKEREV(OMAP4430_DEV, 2, 2, 0) #define OMAP4430_REV_ES2_3 OMAP_MAKEREV(OMAP4430_DEV, 2, 3, 0) #define OMAP4430_REV_UNKNOWN OMAP_MAKEREV(OMAP4430_DEV, 9, 9, 9) #define OMAP4460_DEV 0x4460 #define OMAP4460_REV_ES1_0 OMAP_MAKEREV(OMAP4460_DEV, 1, 0, 0) #define OMAP4460_REV_ES1_1 OMAP_MAKEREV(OMAP4460_DEV, 1, 1, 0) #define OMAP4460_REV_UNKNOWN OMAP_MAKEREV(OMAP4460_DEV, 9, 9, 9) #define OMAP4470_DEV 0x4470 #define OMAP4470_REV_ES1_0 OMAP_MAKEREV(OMAP4470_DEV, 1, 0, 0) #define OMAP4470_REV_UNKNOWN OMAP_MAKEREV(OMAP4470_DEV, 9, 9, 9) #define OMAP_UNKNOWN_DEV OMAP_MAKEREV(0x9999, 9, 9, 9) #define AM335X_DEVREV(x) ((x) >> 28) -#define CHIP_OMAP_3 0 -#define CHIP_OMAP_4 1 -#define CHIP_AM335X 2 +#define CHIP_OMAP_4 0 +#define CHIP_AM335X 1 static __inline int ti_chip(void) { #if defined(SOC_OMAP4) return CHIP_OMAP_4; -#elif defined(SOC_OMAP3) - return CHIP_OMAP_3; #elif defined(SOC_TI_AM335X) return CHIP_AM335X; #else # error Chip type not defined, ensure SOC_xxxx is defined #endif } uint32_t ti_revision(void); #endif /* _TI_CPUID_H_ */ Index: head/sys/arm/ti/ti_gpio.c =================================================================== --- head/sys/arm/ti/ti_gpio.c (revision 273040) +++ head/sys/arm/ti/ti_gpio.c (revision 273041) @@ -1,985 +1,940 @@ /*- * 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. */ /** * Very simple GPIO (general purpose IO) driver module for TI OMAP SoC's. * * Currently this driver only does the basics, get a value on a pin & set a * value on a pin. Hopefully over time I'll expand this to be a bit more generic * and support interrupts and other various bits on the SoC can do ... in the * meantime this is all you get. * * Beware the OMA datasheet(s) lists GPIO banks 1-6, whereas I've used 0-5 here * in the code. * * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" /* Register definitions */ #define TI_GPIO_REVISION 0x0000 #define TI_GPIO_SYSCONFIG 0x0010 -#if defined(SOC_OMAP3) -#define TI_GPIO_SYSSTATUS 0x0014 -#define TI_GPIO_IRQSTATUS1 0x0018 -#define TI_GPIO_IRQENABLE1 0x001C -#define TI_GPIO_WAKEUPENABLE 0x0020 -#define TI_GPIO_IRQSTATUS2 0x0028 -#define TI_GPIO_IRQENABLE2 0x002C -#define TI_GPIO_CTRL 0x0030 -#define TI_GPIO_OE 0x0034 -#define TI_GPIO_DATAIN 0x0038 -#define TI_GPIO_DATAOUT 0x003C -#define TI_GPIO_LEVELDETECT0 0x0040 -#define TI_GPIO_LEVELDETECT1 0x0044 -#define TI_GPIO_RISINGDETECT 0x0048 -#define TI_GPIO_FALLINGDETECT 0x004C -#define TI_GPIO_DEBOUNCENABLE 0x0050 -#define TI_GPIO_DEBOUNCINGTIME 0x0054 -#define TI_GPIO_CLEARIRQENABLE1 0x0060 -#define TI_GPIO_SETIRQENABLE1 0x0064 -#define TI_GPIO_CLEARIRQENABLE2 0x0070 -#define TI_GPIO_SETIRQENABLE2 0x0074 -#define TI_GPIO_CLEARWKUENA 0x0080 -#define TI_GPIO_SETWKUENA 0x0084 -#define TI_GPIO_CLEARDATAOUT 0x0090 -#define TI_GPIO_SETDATAOUT 0x0094 -#elif defined(SOC_OMAP4) || defined(SOC_TI_AM335X) +#if defined(SOC_OMAP4) || defined(SOC_TI_AM335X) #define TI_GPIO_IRQSTATUS_RAW_0 0x0024 #define TI_GPIO_IRQSTATUS_RAW_1 0x0028 #define TI_GPIO_IRQSTATUS_0 0x002C #define TI_GPIO_IRQSTATUS_1 0x0030 #define TI_GPIO_IRQSTATUS_SET_0 0x0034 #define TI_GPIO_IRQSTATUS_SET_1 0x0038 #define TI_GPIO_IRQSTATUS_CLR_0 0x003C #define TI_GPIO_IRQSTATUS_CLR_1 0x0040 #define TI_GPIO_IRQWAKEN_0 0x0044 #define TI_GPIO_IRQWAKEN_1 0x0048 #define TI_GPIO_SYSSTATUS 0x0114 #define TI_GPIO_IRQSTATUS1 0x0118 #define TI_GPIO_IRQENABLE1 0x011C #define TI_GPIO_WAKEUPENABLE 0x0120 #define TI_GPIO_IRQSTATUS2 0x0128 #define TI_GPIO_IRQENABLE2 0x012C #define TI_GPIO_CTRL 0x0130 #define TI_GPIO_OE 0x0134 #define TI_GPIO_DATAIN 0x0138 #define TI_GPIO_DATAOUT 0x013C #define TI_GPIO_LEVELDETECT0 0x0140 #define TI_GPIO_LEVELDETECT1 0x0144 #define TI_GPIO_RISINGDETECT 0x0148 #define TI_GPIO_FALLINGDETECT 0x014C #define TI_GPIO_DEBOUNCENABLE 0x0150 #define TI_GPIO_DEBOUNCINGTIME 0x0154 #define TI_GPIO_CLEARWKUPENA 0x0180 #define TI_GPIO_SETWKUENA 0x0184 #define TI_GPIO_CLEARDATAOUT 0x0190 #define TI_GPIO_SETDATAOUT 0x0194 #else #error "Unknown SoC" #endif /* Other SoC Specific definitions */ -#define OMAP3_MAX_GPIO_BANKS 6 -#define OMAP3_FIRST_GPIO_BANK 1 -#define OMAP3_INTR_PER_BANK 1 -#define OMAP3_GPIO_REV 0x00000025 #define OMAP4_MAX_GPIO_BANKS 6 #define OMAP4_FIRST_GPIO_BANK 1 #define OMAP4_INTR_PER_BANK 1 #define OMAP4_GPIO_REV 0x50600801 #define AM335X_MAX_GPIO_BANKS 4 #define AM335X_FIRST_GPIO_BANK 0 #define AM335X_INTR_PER_BANK 2 #define AM335X_GPIO_REV 0x50600801 #define PINS_PER_BANK 32 #define MAX_GPIO_BANKS 6 /* Maximum GPIOS possible, max of *_MAX_GPIO_BANKS * *_INTR_PER_BANK */ #define MAX_GPIO_INTRS 8 static u_int ti_max_gpio_banks(void) { switch(ti_chip()) { -#ifdef SOC_OMAP3 - case CHIP_OMAP_3: - return (OMAP3_MAX_GPIO_BANKS); -#endif #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_MAX_GPIO_BANKS); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_MAX_GPIO_BANKS); #endif } return (0); } static u_int ti_max_gpio_intrs(void) { switch(ti_chip()) { -#ifdef SOC_OMAP3 - case CHIP_OMAP_3: - return (OMAP3_MAX_GPIO_BANKS * OMAP3_INTR_PER_BANK); -#endif #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_MAX_GPIO_BANKS * OMAP4_INTR_PER_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_MAX_GPIO_BANKS * AM335X_INTR_PER_BANK); #endif } return (0); } static u_int ti_first_gpio_bank(void) { switch(ti_chip()) { -#ifdef SOC_OMAP3 - case CHIP_OMAP_3: - return (OMAP3_FIRST_GPIO_BANK); -#endif #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_FIRST_GPIO_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_FIRST_GPIO_BANK); #endif } return (0); } static uint32_t ti_gpio_rev(void) { switch(ti_chip()) { -#ifdef SOC_OMAP3 - case CHIP_OMAP_3: - return (OMAP3_GPIO_REV); -#endif #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_GPIO_REV); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_GPIO_REV); #endif } return (0); } /** * ti_gpio_mem_spec - Resource specification used when allocating resources * ti_gpio_irq_spec - Resource specification used when allocating resources * * This driver module can have up to six independent memory regions, each * region typically controls 32 GPIO pins. * * On OMAP3 and OMAP4 there is only one physical interrupt line per bank, * but there are two set of registers which control the interrupt delivery * to internal subsystems. The first set of registers control the * interrupts delivery to the MPU and the second set control the * interrupts delivery to the DSP. * * On AM335x there are two physical interrupt lines for each GPIO module. * Each interrupt line is controlled by a set of registers. */ static struct resource_spec ti_gpio_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_OPTIONAL }, #if !defined(SOC_TI_AM335X) { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_OPTIONAL }, #endif { -1, 0, 0 } }; static struct resource_spec ti_gpio_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, #if defined(SOC_TI_AM335X) { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL }, #endif { -1, 0, 0 } }; /** * Structure that stores the driver context. * * This structure is allocated during driver attach. */ struct ti_gpio_softc { device_t sc_dev; /* * The memory resource(s) for the PRCM register set, when the device is * created the caller can assign up to 6 memory regions depending on * the SoC type. */ struct resource *sc_mem_res[MAX_GPIO_BANKS]; struct resource *sc_irq_res[MAX_GPIO_INTRS]; /* The handle for the register IRQ handlers. */ void *sc_irq_hdl[MAX_GPIO_INTRS]; /* * The following describes the H/W revision of each of the GPIO banks. */ uint32_t sc_revision[MAX_GPIO_BANKS]; struct mtx sc_mtx; }; /** * Macros for driver mutex locking */ #define TI_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define TI_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define TI_GPIO_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "ti_gpio", MTX_DEF) #define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) #define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) /** * ti_gpio_read_4 - reads a 16-bit value from one of the PADCONFS registers * @sc: GPIO device context * @bank: The bank to read from * @off: The offset of a register from the GPIO register address range * * * RETURNS: * 32-bit value read from the register. */ static inline uint32_t ti_gpio_read_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off) { return (bus_read_4(sc->sc_mem_res[bank], off)); } /** * ti_gpio_write_4 - writes a 32-bit value to one of the PADCONFS registers * @sc: GPIO device context * @bank: The bank to write to * @off: The offset of a register from the GPIO register address range * @val: The value to write into the register * * RETURNS: * nothing */ static inline void ti_gpio_write_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res[bank], off, val); } static inline void ti_gpio_intr_clr(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask) { /* We clear both set of registers. */ #if defined(SOC_OMAP4) || defined(SOC_TI_AM335X) ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_CLR_0, mask); ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_CLR_1, mask); #else ti_gpio_write_4(sc, bank, TI_GPIO_CLEARIRQENABLE1, mask); ti_gpio_write_4(sc, bank, TI_GPIO_CLEARIRQENABLE2, mask); #endif } /** * ti_gpio_pin_max - Returns the maximum number of GPIO pins * @dev: gpio device handle * @maxpin: pointer to a value that upon return will contain the maximum number * of pins in the device. * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_max(device_t dev, int *maxpin) { struct ti_gpio_softc *sc = device_get_softc(dev); unsigned int i; unsigned int banks = 0; TI_GPIO_LOCK(sc); /* Calculate how many valid banks we have and then multiply that by 32 to * give use the total number of pins. */ for (i = 0; i < ti_max_gpio_banks(); i++) { if (sc->sc_mem_res[i] != NULL) banks++; } *maxpin = (banks * PINS_PER_BANK) - 1; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getcaps - Gets the capabilties of a given pin * @dev: gpio device handle * @pin: the number of the pin * @caps: pointer to a value that upon return will contain the capabilities * * Currently all pins have the same capability, notably: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getflags - Gets the current flags of a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: upon return will contain the current flags of the pin * * Reads the current flags of a given pin, here we actually read the H/W * registers to determine the flags, rather than storing the value in the * setflags call. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* Get the current pin state */ ti_scm_padconf_get_gpioflags(pin, flags); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getname - Gets the name of a given pin * @dev: gpio device handle * @pin: the number of the pin * @name: buffer to put the name in * * The driver simply calls the pins gpio_n, where 'n' is obviously the number * of the pin. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* Set a very simple name */ snprintf(name, GPIOMAXNAME, "gpio_%u", pin); name[GPIOMAXNAME - 1] = '\0'; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_setflags - Sets the flags for a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: the flags to set * * The flags of the pin correspond to things like input/output mode, pull-ups, * pull-downs, etc. This driver doesn't support all flags, only the following: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); uint32_t mask = (1UL << (pin % PINS_PER_BANK)); uint32_t reg_val; /* Sanity check the flags supplied are valid, i.e. not input and output */ if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 0x0000) return (EINVAL); if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) return (EINVAL); if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) == (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) return (EINVAL); TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* Set the GPIO mode and state */ if (ti_scm_padconf_set_gpioflags(pin, flags) != 0) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* If configuring as an output set the "output enable" bit */ reg_val = ti_gpio_read_4(sc, bank, TI_GPIO_OE); if (flags & GPIO_PIN_INPUT) reg_val |= mask; else reg_val &= ~mask; ti_gpio_write_4(sc, bank, TI_GPIO_OE, reg_val); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_set - Sets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: non-zero value will drive the pin high, otherwise the pin is * driven low. * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); uint32_t mask = (1UL << (pin % PINS_PER_BANK)); TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } ti_gpio_write_4(sc, bank, (value == GPIO_PIN_LOW) ? TI_GPIO_CLEARDATAOUT : TI_GPIO_SETDATAOUT, mask); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_get - Gets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: pointer to a value that upond return will contain the pin value * * The pin must be configured as an input pin beforehand, otherwise this * function will fail. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); uint32_t mask = (1UL << (pin % PINS_PER_BANK)); uint32_t val = 0; TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* Sanity check the pin is not configured as an output */ val = ti_gpio_read_4(sc, bank, TI_GPIO_OE); /* Read the value on the pin */ if (val & mask) *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAIN) & mask) ? 1 : 0; else *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT) & mask) ? 1 : 0; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_toggle - Toggles a given GPIO pin * @dev: gpio device handle * @pin: the number of the pin * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_toggle(device_t dev, uint32_t pin) { struct ti_gpio_softc *sc = device_get_softc(dev); uint32_t bank = (pin / PINS_PER_BANK); uint32_t mask = (1UL << (pin % PINS_PER_BANK)); uint32_t val; TI_GPIO_LOCK(sc); /* Sanity check the pin number is valid */ if ((bank >= ti_max_gpio_banks()) || (sc->sc_mem_res[bank] == NULL)) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* Toggle the pin */ val = ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT); if (val & mask) ti_gpio_write_4(sc, bank, TI_GPIO_CLEARDATAOUT, mask); else ti_gpio_write_4(sc, bank, TI_GPIO_SETDATAOUT, mask); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_intr - ISR for all GPIO modules * @arg: the soft context pointer * * Unsused * * LOCKING: * Internally locks the context * */ static void ti_gpio_intr(void *arg) { struct ti_gpio_softc *sc = arg; TI_GPIO_LOCK(sc); /* TODO: something useful */ TI_GPIO_UNLOCK(sc); } /** * ti_gpio_probe - probe function for the driver * @dev: gpio device handle * * Simply sets the name of the driver * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,gpio")) return (ENXIO); device_set_desc(dev, "TI General Purpose I/O (GPIO)"); return (0); } static int ti_gpio_attach_intr(device_t dev) { int i; struct ti_gpio_softc *sc; sc = device_get_softc(dev); for (i = 0; i < ti_max_gpio_intrs(); i++) { if (sc->sc_irq_res[i] == NULL) break; /* * Register our interrupt handler for each of the IRQ resources. */ if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_gpio_intr, sc, &sc->sc_irq_hdl[i]) != 0) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); return (-1); } } return (0); } static int ti_gpio_detach_intr(device_t dev) { int i; struct ti_gpio_softc *sc; /* Teardown our interrupt handlers. */ sc = device_get_softc(dev); for (i = 0; i < ti_max_gpio_intrs(); i++) { if (sc->sc_irq_res[i] == NULL) break; if (sc->sc_irq_hdl[i]) { bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_irq_hdl[i]); } } return (0); } static int ti_gpio_bank_init(device_t dev, int bank) { int pin; struct ti_gpio_softc *sc; uint32_t flags, reg_oe; sc = device_get_softc(dev); /* Enable the interface and functional clocks for the module. */ ti_prcm_clk_enable(GPIO0_CLK + ti_first_gpio_bank() + bank); /* * Read the revision number of the module. TI don't publish the * actual revision numbers, so instead the values have been * determined by experimentation. */ sc->sc_revision[bank] = ti_gpio_read_4(sc, bank, TI_GPIO_REVISION); /* Check the revision. */ if (sc->sc_revision[bank] != ti_gpio_rev()) { device_printf(dev, "Warning: could not determine the revision " "of %u GPIO module (revision:0x%08x)\n", bank, sc->sc_revision[bank]); return (EINVAL); } /* Disable interrupts for all pins. */ ti_gpio_intr_clr(sc, bank, 0xffffffff); /* Init OE register based on pads configuration. */ reg_oe = 0xffffffff; for (pin = 0; pin < PINS_PER_BANK; pin++) { ti_scm_padconf_get_gpioflags(PINS_PER_BANK * bank + pin, &flags); if (flags & GPIO_PIN_OUTPUT) reg_oe &= ~(1UL << pin); } ti_gpio_write_4(sc, bank, TI_GPIO_OE, reg_oe); return (0); } /** * ti_gpio_attach - attach function for the driver * @dev: gpio device handle * * Allocates and sets up the driver context for all GPIO banks. This function * expects the memory ranges and IRQs to already be allocated to the driver. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_attach(device_t dev) { struct ti_gpio_softc *sc; unsigned int i; int err; sc = device_get_softc(dev); sc->sc_dev = dev; TI_GPIO_LOCK_INIT(sc); /* There are up to 6 different GPIO register sets located in different * memory areas on the chip. The memory range should have been set for * the driver when it was added as a child. */ if (bus_alloc_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res) != 0) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources */ if (bus_alloc_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res) != 0) { bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* Setup the IRQ resources */ if (ti_gpio_attach_intr(dev) != 0) { ti_gpio_detach_intr(dev); bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); return (ENXIO); } /* We need to go through each block and ensure the clocks are running and * the module is enabled. It might be better to do this only when the * pins are configured which would result in less power used if the GPIO * pins weren't used ... */ for (i = 0; i < ti_max_gpio_banks(); i++) { if (sc->sc_mem_res[i] != NULL) { /* Initialize the GPIO module. */ err = ti_gpio_bank_init(dev, i); if (err != 0) { ti_gpio_detach_intr(dev); bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); return (err); } } } /* Finish of the probe call */ device_add_child(dev, "gpioc", device_get_unit(dev)); device_add_child(dev, "gpiobus", device_get_unit(dev)); return (bus_generic_attach(dev)); } /** * ti_gpio_detach - detach function for the driver * @dev: scm device handle * * Allocates and sets up the driver context, this simply entails creating a * bus mappings for the SCM register set. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_detach(device_t dev) { struct ti_gpio_softc *sc = device_get_softc(dev); unsigned int i; KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); /* Disable all interrupts */ for (i = 0; i < ti_max_gpio_banks(); i++) { if (sc->sc_mem_res[i] != NULL) ti_gpio_intr_clr(sc, i, 0xffffffff); } bus_generic_detach(dev); /* Release the memory and IRQ resources. */ ti_gpio_detach_intr(dev); bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); TI_GPIO_LOCK_DESTROY(sc); return (0); } static phandle_t ti_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_gpio_methods[] = { DEVMETHOD(device_probe, ti_gpio_probe), DEVMETHOD(device_attach, ti_gpio_attach), DEVMETHOD(device_detach, ti_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, ti_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), {0, 0}, }; static driver_t ti_gpio_driver = { "gpio", ti_gpio_methods, sizeof(struct ti_gpio_softc), }; static devclass_t ti_gpio_devclass; DRIVER_MODULE(ti_gpio, simplebus, ti_gpio_driver, ti_gpio_devclass, 0, 0); Index: head/sys/arm/ti/ti_i2c.c =================================================================== --- head/sys/arm/ti/ti_i2c.c (revision 273040) +++ head/sys/arm/ti/ti_i2c.c (revision 273041) @@ -1,967 +1,963 @@ /*- * Copyright (c) 2011 Ben Gray . * Copyright (c) 2014 Luiz Otavio O Souza . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * Driver for the I2C module on the TI SoC. * * This driver is heavily based on the TWI driver for the AT91 (at91_twi.c). * * CAUTION: The I2Ci registers are limited to 16 bit and 8 bit data accesses, * 32 bit data access is not allowed and can corrupt register content. * * This driver currently doesn't use DMA for the transfer, although I hope to * incorporate that sometime in the future. The idea being that for transaction * larger than a certain size the DMA engine is used, for anything less the * normal interrupt/fifo driven option is used. * * * WARNING: This driver uses mtx_sleep and interrupts to perform transactions, * which means you can't do a transaction during startup before the interrupts * have been enabled. Hint - the freebsd function config_intrhook_establish(). */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" /** * I2C device driver context, a pointer to this is stored in the device * driver structure. */ struct ti_i2c_softc { device_t sc_dev; uint32_t device_id; struct resource* sc_irq_res; struct resource* sc_mem_res; device_t sc_iicbus; void* sc_irq_h; struct mtx sc_mtx; struct iic_msg* sc_buffer; int sc_bus_inuse; int sc_buffer_pos; int sc_error; int sc_fifo_trsh; uint16_t sc_con_reg; uint16_t sc_rev; }; struct ti_i2c_clock_config { int speed; int bitrate; uint8_t psc; /* Fast/Standard mode prescale divider */ uint8_t scll; /* Fast/Standard mode SCL low time */ uint8_t sclh; /* Fast/Standard mode SCL high time */ uint8_t hsscll; /* High Speed mode SCL low time */ uint8_t hssclh; /* High Speed mode SCL high time */ }; -#if defined(SOC_OMAP3) -#error "Unsupported SoC" -#endif - #if defined(SOC_OMAP4) static struct ti_i2c_clock_config ti_omap4_i2c_clock_configs[] = { { IIC_UNKNOWN, 100000, 23, 13, 15, 0, 0}, { IIC_SLOW, 100000, 23, 13, 15, 0, 0}, { IIC_FAST, 400000, 9, 5, 7, 0, 0}, { IIC_FASTEST, 1000000, 5, 3, 4, 0, 0}, /* { IIC_FASTEST, 3200000, 1, 113, 115, 7, 10}, - HS mode */ { -1, 0 } }; #endif #if defined(SOC_TI_AM335X) /* * AM335X doesn't support HS mode. For 100kHz I2C clock set the internal * clock to 12Mhz, for 400kHz I2C clock set the internal clock to 24Mhz. */ static struct ti_i2c_clock_config ti_am335x_i2c_clock_configs[] = { { IIC_UNKNOWN, 100000, 7, 59, 61, 0, 0}, { IIC_SLOW, 100000, 7, 59, 61, 0, 0}, { IIC_FAST, 400000, 3, 23, 25, 0, 0}, { IIC_FASTEST, 400000, 3, 23, 25, 0, 0}, { -1, 0 } }; #endif /** * Locking macros used throughout the driver */ #define TI_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define TI_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define TI_I2C_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "ti_i2c", MTX_DEF) #define TI_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) #define TI_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define TI_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) #ifdef DEBUG #define ti_i2c_dbg(_sc, fmt, args...) \ device_printf((_sc)->sc_dev, fmt, ##args) #else #define ti_i2c_dbg(_sc, fmt, args...) #endif /** * ti_i2c_read_2 - reads a 16-bit value from one of the I2C registers * @sc: I2C device context * @off: the byte offset within the register bank to read from. * * * LOCKING: * No locking required * * RETURNS: * 16-bit value read from the register. */ static inline uint16_t ti_i2c_read_2(struct ti_i2c_softc *sc, bus_size_t off) { return (bus_read_2(sc->sc_mem_res, off)); } /** * ti_i2c_write_2 - writes a 16-bit value to one of the I2C registers * @sc: I2C device context * @off: the byte offset within the register bank to read from. * @val: the value to write into the register * * LOCKING: * No locking required * * RETURNS: * 16-bit value read from the register. */ static inline void ti_i2c_write_2(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) { bus_write_2(sc->sc_mem_res, off, val); } static int ti_i2c_transfer_intr(struct ti_i2c_softc* sc, uint16_t status) { int amount, done, i; done = 0; amount = 0; /* Check for the error conditions. */ if (status & I2C_STAT_NACK) { /* No ACK from slave. */ ti_i2c_dbg(sc, "NACK\n"); ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_NACK); sc->sc_error = ENXIO; } else if (status & I2C_STAT_AL) { /* Arbitration lost. */ ti_i2c_dbg(sc, "Arbitration lost\n"); ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_AL); sc->sc_error = ENXIO; } /* Check if we have finished. */ if (status & I2C_STAT_ARDY) { /* Register access ready - transaction complete basically. */ ti_i2c_dbg(sc, "ARDY transaction complete\n"); if (sc->sc_error != 0 && sc->sc_buffer->flags & IIC_M_NOSTOP) { ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg | I2C_CON_STP); } ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_ARDY | I2C_STAT_RDR | I2C_STAT_RRDY | I2C_STAT_XDR | I2C_STAT_XRDY); return (1); } if (sc->sc_buffer->flags & IIC_M_RD) { /* Read some data. */ if (status & I2C_STAT_RDR) { /* * Receive draining interrupt - last data received. * The set FIFO threshold wont be reached to trigger * RRDY. */ ti_i2c_dbg(sc, "Receive draining interrupt\n"); /* * Drain the FIFO. Read the pending data in the FIFO. */ amount = sc->sc_buffer->len - sc->sc_buffer_pos; } else if (status & I2C_STAT_RRDY) { /* * Receive data ready interrupt - FIFO has reached the * set threshold. */ ti_i2c_dbg(sc, "Receive data ready interrupt\n"); amount = min(sc->sc_fifo_trsh, sc->sc_buffer->len - sc->sc_buffer_pos); } /* Read the bytes from the fifo. */ for (i = 0; i < amount; i++) sc->sc_buffer->buf[sc->sc_buffer_pos++] = (uint8_t)(ti_i2c_read_2(sc, I2C_REG_DATA) & 0xff); if (status & I2C_STAT_RDR) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RDR); if (status & I2C_STAT_RRDY) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_RRDY); } else { /* Write some data. */ if (status & I2C_STAT_XDR) { /* * Transmit draining interrupt - FIFO level is below * the set threshold and the amount of data still to * be transferred wont reach the set FIFO threshold. */ ti_i2c_dbg(sc, "Transmit draining interrupt\n"); /* * Drain the TX data. Write the pending data in the * FIFO. */ amount = sc->sc_buffer->len - sc->sc_buffer_pos; } else if (status & I2C_STAT_XRDY) { /* * Transmit data ready interrupt - the FIFO level * is below the set threshold. */ ti_i2c_dbg(sc, "Transmit data ready interrupt\n"); amount = min(sc->sc_fifo_trsh, sc->sc_buffer->len - sc->sc_buffer_pos); } /* Write the bytes from the fifo. */ for (i = 0; i < amount; i++) ti_i2c_write_2(sc, I2C_REG_DATA, sc->sc_buffer->buf[sc->sc_buffer_pos++]); if (status & I2C_STAT_XDR) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XDR); if (status & I2C_STAT_XRDY) ti_i2c_write_2(sc, I2C_REG_STATUS, I2C_STAT_XRDY); } return (done); } /** * ti_i2c_intr - interrupt handler for the I2C module * @dev: i2c device handle * * * * LOCKING: * Called from timer context * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ static void ti_i2c_intr(void *arg) { int done; struct ti_i2c_softc *sc; uint16_t events, status; sc = (struct ti_i2c_softc *)arg; TI_I2C_LOCK(sc); status = ti_i2c_read_2(sc, I2C_REG_STATUS); if (status == 0) { TI_I2C_UNLOCK(sc); return; } /* Save enabled interrupts. */ events = ti_i2c_read_2(sc, I2C_REG_IRQENABLE_SET); /* We only care about enabled interrupts. */ status &= events; done = 0; if (sc->sc_buffer != NULL) done = ti_i2c_transfer_intr(sc, status); else { ti_i2c_dbg(sc, "Transfer interrupt without buffer\n"); sc->sc_error = EINVAL; done = 1; } if (done) /* Wakeup the process that started the transaction. */ wakeup(sc); TI_I2C_UNLOCK(sc); } /** * ti_i2c_transfer - called to perform the transfer * @dev: i2c device handle * @msgs: the messages to send/receive * @nmsgs: the number of messages in the msgs array * * * LOCKING: * Internally locked * * RETURNS: * 0 on function succeeded * EINVAL if invalid message is passed as an arg */ static int ti_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int err, i, repstart, timeout; struct ti_i2c_softc *sc; uint16_t reg; sc = device_get_softc(dev); TI_I2C_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_bus_inuse == 1) mtx_sleep(dev, &sc->sc_mtx, 0, "i2cbuswait", 0); /* Now we have control over the I2C controller. */ sc->sc_bus_inuse = 1; err = 0; repstart = 0; for (i = 0; i < nmsgs; i++) { sc->sc_buffer = &msgs[i]; sc->sc_buffer_pos = 0; sc->sc_error = 0; /* Zero byte transfers aren't allowed. */ if (sc->sc_buffer == NULL || sc->sc_buffer->buf == NULL || sc->sc_buffer->len == 0) { err = EINVAL; break; } /* Check if the i2c bus is free. */ if (repstart == 0) { /* * On repeated start we send the START condition while * the bus _is_ busy. */ timeout = 0; while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) { if (timeout++ > 100) { err = EBUSY; goto out; } DELAY(1000); } timeout = 0; } else repstart = 0; if (sc->sc_buffer->flags & IIC_M_NOSTOP) repstart = 1; /* Set the slave address. */ ti_i2c_write_2(sc, I2C_REG_SA, msgs[i].slave >> 1); /* Write the data length. */ ti_i2c_write_2(sc, I2C_REG_CNT, sc->sc_buffer->len); /* Clear the RX and the TX FIFO. */ reg = ti_i2c_read_2(sc, I2C_REG_BUF); reg |= I2C_BUF_RXFIFO_CLR | I2C_BUF_TXFIFO_CLR; ti_i2c_write_2(sc, I2C_REG_BUF, reg); reg = sc->sc_con_reg | I2C_CON_STT; if (repstart == 0) reg |= I2C_CON_STP; if ((sc->sc_buffer->flags & IIC_M_RD) == 0) reg |= I2C_CON_TRX; ti_i2c_write_2(sc, I2C_REG_CON, reg); /* Wait for an event. */ err = mtx_sleep(sc, &sc->sc_mtx, 0, "i2ciowait", hz); if (err == 0) err = sc->sc_error; if (err) break; } out: if (timeout == 0) { while (ti_i2c_read_2(sc, I2C_REG_STATUS_RAW) & I2C_STAT_BB) { if (timeout++ > 100) break; DELAY(1000); } } /* Put the controller in master mode again. */ if ((ti_i2c_read_2(sc, I2C_REG_CON) & I2C_CON_MST) == 0) ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); sc->sc_buffer = NULL; sc->sc_bus_inuse = 0; /* Wake up the processes that are waiting for the bus. */ wakeup(sc); TI_I2C_UNLOCK(sc); return (err); } /** * ti_i2c_callback - as we only provide iicbus_transfer() interface * we don't need to implement the serialization here. * @dev: i2c device handle * * * * LOCKING: * Called from timer context * * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ static int ti_i2c_callback(device_t dev, int index, caddr_t data) { int error = 0; switch (index) { case IIC_REQUEST_BUS: break; case IIC_RELEASE_BUS: break; default: error = EINVAL; } return (error); } static int ti_i2c_reset(struct ti_i2c_softc *sc, u_char speed) { int timeout; struct ti_i2c_clock_config *clkcfg; uint16_t fifo_trsh, reg, scll, sclh; switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: clkcfg = ti_omap4_i2c_clock_configs; break; #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: clkcfg = ti_am335x_i2c_clock_configs; break; #endif default: panic("Unknown Ti SoC, unable to reset the i2c"); } while (clkcfg->speed != -1) { if (clkcfg->speed == speed) break; clkcfg++; } if (clkcfg->speed == -1) return (EINVAL); /* * 23.1.4.3 - HS I2C Software Reset * From OMAP4 TRM at page 4068. * * 1. Ensure that the module is disabled. */ sc->sc_con_reg = 0; ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* 2. Issue a softreset to the controller. */ bus_write_2(sc->sc_mem_res, I2C_REG_SYSC, I2C_REG_SYSC_SRST); /* * 3. Enable the module. * The I2Ci.I2C_SYSS[0] RDONE bit is asserted only after the module * is enabled by setting the I2Ci.I2C_CON[15] I2C_EN bit to 1. */ ti_i2c_write_2(sc, I2C_REG_CON, I2C_CON_I2C_EN); /* 4. Wait for the software reset to complete. */ timeout = 0; while ((ti_i2c_read_2(sc, I2C_REG_SYSS) & I2C_SYSS_RDONE) == 0) { if (timeout++ > 100) return (EBUSY); DELAY(100); } /* * Disable the I2C controller once again, now that the reset has * finished. */ ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* * The following sequence is taken from the OMAP4 TRM at page 4077. * * 1. Enable the functional and interface clocks (see Section * 23.1.5.1.1.1.1). Done at ti_i2c_activate(). * * 2. Program the prescaler to obtain an approximately 12MHz internal * sampling clock (I2Ci_INTERNAL_CLK) by programming the * corresponding value in the I2Ci.I2C_PSC[3:0] PSC field. * This value depends on the frequency of the functional clock * (I2Ci_FCLK). Because this frequency is 96MHz, the * I2Ci.I2C_PSC[7:0] PSC field value is 0x7. */ ti_i2c_write_2(sc, I2C_REG_PSC, clkcfg->psc); /* * 3. Program the I2Ci.I2C_SCLL[7:0] SCLL and I2Ci.I2C_SCLH[7:0] SCLH * bit fields to obtain a bit rate of 100 Kbps, 400 Kbps or 1Mbps. * These values depend on the internal sampling clock frequency * (see Table 23-8). */ scll = clkcfg->scll & I2C_SCLL_MASK; sclh = clkcfg->sclh & I2C_SCLH_MASK; /* * 4. (Optional) Program the I2Ci.I2C_SCLL[15:8] HSSCLL and * I2Ci.I2C_SCLH[15:8] HSSCLH fields to obtain a bit rate of * 400K bps or 3.4M bps (for the second phase of HS mode). These * values depend on the internal sampling clock frequency (see * Table 23-8). * * 5. (Optional) If a bit rate of 3.4M bps is used and the bus line * capacitance exceeds 45 pF, (see Section 18.4.8, PAD Functional * Multiplexing and Configuration). */ switch (ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: if ((clkcfg->hsscll + clkcfg->hssclh) > 0) { scll |= clkcfg->hsscll << I2C_HSSCLL_SHIFT; sclh |= clkcfg->hssclh << I2C_HSSCLH_SHIFT; sc->sc_con_reg |= I2C_CON_OPMODE_HS; } break; #endif } /* Write the selected bit rate. */ ti_i2c_write_2(sc, I2C_REG_SCLL, scll); ti_i2c_write_2(sc, I2C_REG_SCLH, sclh); /* * 6. Configure the Own Address of the I2C controller by storing it in * the I2Ci.I2C_OA0 register. Up to four Own Addresses can be * programmed in the I2Ci.I2C_OAi registers (where i = 0, 1, 2, 3) * for each I2C controller. * * Note: For a 10-bit address, set the corresponding expand Own Address * bit in the I2Ci.I2C_CON register. * * Driver currently always in single master mode so ignore this step. */ /* * 7. Set the TX threshold (in transmitter mode) and the RX threshold * (in receiver mode) by setting the I2Ci.I2C_BUF[5:0]XTRSH field to * (TX threshold - 1) and the I2Ci.I2C_BUF[13:8]RTRSH field to (RX * threshold - 1), where the TX and RX thresholds are greater than * or equal to 1. * * The threshold is set to 5 for now. */ fifo_trsh = (sc->sc_fifo_trsh - 1) & I2C_BUF_TRSH_MASK; reg = fifo_trsh | (fifo_trsh << I2C_BUF_RXTRSH_SHIFT); ti_i2c_write_2(sc, I2C_REG_BUF, reg); /* * 8. Take the I2C controller out of reset by setting the * I2Ci.I2C_CON[15] I2C_EN bit to 1. * * 23.1.5.1.1.1.2 - Initialize the I2C Controller * * To initialize the I2C controller, perform the following steps: * * 1. Configure the I2Ci.I2C_CON register: * . For master or slave mode, set the I2Ci.I2C_CON[10] MST bit * (0: slave, 1: master). * . For transmitter or receiver mode, set the I2Ci.I2C_CON[9] TRX * bit (0: receiver, 1: transmitter). */ /* Enable the I2C controller in master mode. */ sc->sc_con_reg |= I2C_CON_I2C_EN | I2C_CON_MST; ti_i2c_write_2(sc, I2C_REG_CON, sc->sc_con_reg); /* * 2. If using an interrupt to transmit/receive data, set the * corresponding bit in the I2Ci.I2C_IE register (the I2Ci.I2C_IE[4] * XRDY_IE bit for the transmit interrupt, the I2Ci.I2C_IE[3] RRDY * bit for the receive interrupt). */ /* Set the interrupts we want to be notified. */ reg = I2C_IE_XDR | /* Transmit draining interrupt. */ I2C_IE_XRDY | /* Transmit Data Ready interrupt. */ I2C_IE_RDR | /* Receive draining interrupt. */ I2C_IE_RRDY | /* Receive Data Ready interrupt. */ I2C_IE_ARDY | /* Register Access Ready interrupt. */ I2C_IE_NACK | /* No Acknowledgment interrupt. */ I2C_IE_AL; /* Arbitration lost interrupt. */ /* Enable the interrupts. */ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_SET, reg); /* * 3. If using DMA to receive/transmit data, set to 1 the corresponding * bit in the I2Ci.I2C_BUF register (the I2Ci.I2C_BUF[15] RDMA_EN * bit for the receive DMA channel, the I2Ci.I2C_BUF[7] XDMA_EN bit * for the transmit DMA channel). * * Not using DMA for now, so ignore this. */ return (0); } static int ti_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct ti_i2c_softc *sc; int err; sc = device_get_softc(dev); TI_I2C_LOCK(sc); err = ti_i2c_reset(sc, speed); TI_I2C_UNLOCK(sc); if (err) return (err); return (IIC_ENOADDR); } static int ti_i2c_activate(device_t dev) { clk_ident_t clk; int err; struct ti_i2c_softc *sc; sc = (struct ti_i2c_softc*)device_get_softc(dev); /* * 1. Enable the functional and interface clocks (see Section * 23.1.5.1.1.1.1). */ clk = I2C0_CLK + sc->device_id; err = ti_prcm_clk_enable(clk); if (err) return (err); return (ti_i2c_reset(sc, IIC_UNKNOWN)); } /** * ti_i2c_deactivate - deactivates the controller and releases resources * @dev: i2c device handle * * * * LOCKING: * Assumed called in an atomic context. * * RETURNS: * nothing */ static void ti_i2c_deactivate(device_t dev) { struct ti_i2c_softc *sc = device_get_softc(dev); clk_ident_t clk; /* Disable the controller - cancel all transactions. */ ti_i2c_write_2(sc, I2C_REG_IRQENABLE_CLR, 0xffff); ti_i2c_write_2(sc, I2C_REG_STATUS, 0xffff); ti_i2c_write_2(sc, I2C_REG_CON, 0); /* Release the interrupt handler. */ if (sc->sc_irq_h != NULL) { bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); sc->sc_irq_h = NULL; } bus_generic_detach(sc->sc_dev); /* Unmap the I2C controller registers. */ if (sc->sc_mem_res != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); sc->sc_mem_res = NULL; } /* Release the IRQ resource. */ if (sc->sc_irq_res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } /* Finally disable the functional and interface clocks. */ clk = I2C0_CLK + sc->device_id; ti_prcm_clk_disable(clk); } static int ti_i2c_sysctl_clk(SYSCTL_HANDLER_ARGS) { device_t dev; int clk, psc, sclh, scll; struct ti_i2c_softc *sc; dev = (device_t)arg1; sc = device_get_softc(dev); TI_I2C_LOCK(sc); /* Get the system prescaler value. */ psc = (int)ti_i2c_read_2(sc, I2C_REG_PSC) + 1; /* Get the bitrate. */ scll = (int)ti_i2c_read_2(sc, I2C_REG_SCLL) & I2C_SCLL_MASK; sclh = (int)ti_i2c_read_2(sc, I2C_REG_SCLH) & I2C_SCLH_MASK; clk = I2C_CLK / psc / (scll + 7 + sclh + 5); TI_I2C_UNLOCK(sc); return (sysctl_handle_int(oidp, &clk, 0, req)); } static int ti_i2c_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,i2c")) return (ENXIO); device_set_desc(dev, "TI I2C Controller"); return (0); } static int ti_i2c_attach(device_t dev) { int err, rid; phandle_t node; struct ti_i2c_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *tree; uint16_t fifosz; sc = device_get_softc(dev); sc->sc_dev = dev; /* Get the i2c device id from FDT. */ node = ofw_bus_get_node(dev); if ((OF_getencprop(node, "i2c-device-id", &sc->device_id, sizeof(sc->device_id))) <= 0) { device_printf(dev, "missing i2c-device-id attribute in FDT\n"); return (ENXIO); } /* Get the memory resource for the register mapping. */ rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "Cannot map registers.\n"); return (ENXIO); } /* Allocate our IRQ resource. */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "Cannot allocate interrupt.\n"); return (ENXIO); } TI_I2C_LOCK_INIT(sc); /* First of all, we _must_ activate the H/W. */ err = ti_i2c_activate(dev); if (err) { device_printf(dev, "ti_i2c_activate failed\n"); goto out; } /* Read the version number of the I2C module */ sc->sc_rev = ti_i2c_read_2(sc, I2C_REG_REVNB_HI) & 0xff; /* Get the fifo size. */ fifosz = ti_i2c_read_2(sc, I2C_REG_BUFSTAT); fifosz >>= I2C_BUFSTAT_FIFODEPTH_SHIFT; fifosz &= I2C_BUFSTAT_FIFODEPTH_MASK; device_printf(dev, "I2C revision %d.%d FIFO size: %d bytes\n", sc->sc_rev >> 4, sc->sc_rev & 0xf, 8 << fifosz); /* Set the FIFO threshold to 5 for now. */ sc->sc_fifo_trsh = 5; ctx = device_get_sysctl_ctx(dev); tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "i2c_clock", CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_MPSAFE, dev, 0, ti_i2c_sysctl_clk, "IU", "I2C bus clock"); /* Activate the interrupt. */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_i2c_intr, sc, &sc->sc_irq_h); if (err) goto out; /* Attach the iicbus. */ if ((sc->sc_iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { device_printf(dev, "could not allocate iicbus instance\n"); err = ENXIO; goto out; } /* Probe and attach the iicbus */ bus_generic_attach(dev); out: if (err) { ti_i2c_deactivate(dev); TI_I2C_LOCK_DESTROY(sc); } return (err); } static int ti_i2c_detach(device_t dev) { struct ti_i2c_softc *sc; int rv; sc = device_get_softc(dev); ti_i2c_deactivate(dev); TI_I2C_LOCK_DESTROY(sc); if (sc->sc_iicbus && (rv = device_delete_child(dev, sc->sc_iicbus)) != 0) return (rv); return (0); } static phandle_t ti_i2c_get_node(device_t bus, device_t dev) { /* Share controller node with iibus device. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_i2c_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_i2c_probe), DEVMETHOD(device_attach, ti_i2c_attach), DEVMETHOD(device_detach, ti_i2c_detach), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, ti_i2c_get_node), /* iicbus interface */ DEVMETHOD(iicbus_callback, ti_i2c_callback), DEVMETHOD(iicbus_reset, ti_i2c_iicbus_reset), DEVMETHOD(iicbus_transfer, ti_i2c_transfer), DEVMETHOD_END }; static driver_t ti_i2c_driver = { "iichb", ti_i2c_methods, sizeof(struct ti_i2c_softc), }; static devclass_t ti_i2c_devclass; DRIVER_MODULE(ti_iic, simplebus, ti_i2c_driver, ti_i2c_devclass, 0, 0); DRIVER_MODULE(iicbus, ti_iic, iicbus_driver, iicbus_devclass, 0, 0); MODULE_DEPEND(ti_iic, ti_prcm, 1, 1, 1); MODULE_DEPEND(ti_iic, iicbus, 1, 1, 1); Index: head/sys/arm/ti/ti_mmchs.c =================================================================== --- head/sys/arm/ti/ti_mmchs.c (revision 273040) +++ head/sys/arm/ti/ti_mmchs.c (revision 273041) @@ -1,1843 +1,1841 @@ /*- * 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. */ /** * Driver for the MMC/SD/SDIO module on the TI OMAP series of SoCs. * * This driver is heavily based on the SD/MMC driver for the AT91 (at91_mci.c). * * It's important to realise that the MMC state machine is already in the kernel * and this driver only exposes the specific interfaces of the controller. * * This driver is still very much a work in progress, I've verified that basic * sector reading can be performed. But I've yet to test it with a file system * or even writing. In addition I've only tested the driver with an SD card, * I've no idea if MMC cards work. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "mmcbr_if.h" #include "mmcbus_if.h" #include #include #include #include #include #include #include #ifdef DEBUG #define ti_mmchs_dbg(sc, fmt, args...) \ device_printf((sc)->sc_dev, fmt, ## args); #else #define ti_mmchs_dbg(sc, fmt, args...) #endif /** * Structure that stores the driver context */ struct ti_mmchs_softc { device_t sc_dev; uint32_t device_id; struct resource* sc_irq_res; struct resource* sc_mem_res; void* sc_irq_h; bus_dma_tag_t sc_dmatag; bus_dmamap_t sc_dmamap; int sc_dmamapped; unsigned int sc_dmach_rd; unsigned int sc_dmach_wr; int dma_rx_trig; int dma_tx_trig; device_t sc_gpio_dev; int sc_wp_gpio_pin; /* GPIO pin for MMC write protect */ device_t sc_vreg_dev; const char* sc_vreg_name; struct mtx sc_mtx; struct mmc_host host; struct mmc_request* req; struct mmc_command* curcmd; int flags; #define CMD_STARTED 1 #define STOP_STARTED 2 int bus_busy; /* TODO: Needed ? */ void* sc_cmd_data_vaddr; int sc_cmd_data_len; /* The offset applied to each of the register base addresses, OMAP4 * register sets are offset 0x100 from the OMAP3 series. */ unsigned long sc_reg_off; /* The physical address of the MMCHS_DATA register, used for the DMA xfers */ unsigned long sc_data_reg_paddr; /* The reference clock frequency */ unsigned int sc_ref_freq; enum mmc_power_mode sc_cur_power_mode; }; /** * Macros for driver mutex locking */ #define TI_MMCHS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define TI_MMCHS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define TI_MMCHS_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ "ti_mmchs", MTX_DEF) #define TI_MMCHS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define TI_MMCHS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define TI_MMCHS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); static void ti_mmchs_start(struct ti_mmchs_softc *sc); /** * ti_mmchs_read_4 - reads a 32-bit value from a register * ti_mmchs_write_4 - writes a 32-bit value to a register * @sc: pointer to the driver context * @off: register offset to read from * @val: the value to write into the register * * LOCKING: * None * * RETURNS: * The 32-bit value read from the register */ static inline uint32_t ti_mmchs_read_4(struct ti_mmchs_softc *sc, bus_size_t off) { return bus_read_4(sc->sc_mem_res, (sc->sc_reg_off + off)); } static inline void ti_mmchs_write_4(struct ti_mmchs_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res, (sc->sc_reg_off + off), val); } /** * ti_mmchs_reset_controller - * @arg: caller supplied arg * @segs: array of segments (although in our case should only be one) * @nsegs: number of segments (in our case should be 1) * @error: * * * */ static void ti_mmchs_reset_controller(struct ti_mmchs_softc *sc, uint32_t bit) { unsigned long attempts; uint32_t sysctl; ti_mmchs_dbg(sc, "reseting controller - bit 0x%08x\n", bit); sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | bit); /* * AM335x and OMAP4 >= ES2 have an updated reset logic. * Monitor a 0->1 transition first. */ if ((ti_chip() == CHIP_AM335X) || ((ti_chip() == CHIP_OMAP_4) && (ti_revision() > OMAP4430_REV_ES1_0))) { attempts = 10000; while (!(ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) continue; } attempts = 10000; while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0)) continue; if (ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) device_printf(sc->sc_dev, "Error - Timeout waiting on controller reset\n"); } /** * ti_mmchs_getaddr - called by the DMA function to simply return the phys addr * @arg: caller supplied arg * @segs: array of segments (although in our case should only be one) * @nsegs: number of segments (in our case should be 1) * @error: * * This function is called by bus_dmamap_load() after it has compiled an array * of segments, each segment is a phsyical chunk of memory. However in our case * we should only have one segment, because we don't (yet?) support DMA scatter * gather. To ensure we only have one segment, the DMA tag was created by * bus_dma_tag_create() (called from ti_mmchs_attach) with nsegments set to 1. * */ static void ti_mmchs_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } #ifndef SOC_TI_AM335X /** * ti_mmchs_dma_intr - interrupt handler for DMA events triggered by the controller * @ch: the dma channel number * @status: bit field of the status bytes * @data: callback data, in this case a pointer to the controller struct * * * LOCKING: * Called from interrupt context * */ static void ti_mmchs_dma_intr(unsigned int ch, uint32_t status, void *data) { /* Ignore for now ... we don't need this interrupt as we already have the * interrupt from the MMC controller. */ } #endif /** * ti_mmchs_intr_xfer_compl - called if a 'transfer complete' IRQ was received * @sc: pointer to the driver context * @cmd: the command that was sent previously * * This function is simply responsible for syncing up the DMA buffer. * * LOCKING: * Called from interrupt context * * RETURNS: * Return value indicates if the transaction is complete, not done = 0, done != 0 */ static int ti_mmchs_intr_xfer_compl(struct ti_mmchs_softc *sc, struct mmc_command *cmd) { uint32_t cmd_reg; /* Read command register to test whether this command was a read or write. */ cmd_reg = ti_mmchs_read_4(sc, MMCHS_CMD); /* Sync-up the DMA buffer so the caller can access the new memory */ if (cmd_reg & MMCHS_CMD_DDIR) { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); } else { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); } sc->sc_dmamapped--; /* Debugging dump of the data received */ #if 0 { int i; uint8_t *p = (uint8_t*) sc->sc_cmd_data_vaddr; for (i=0; isc_cmd_data_len; i++) { if ((i % 16) == 0) printf("\n0x%04x : ", i); printf("%02X ", *p++); } printf("\n"); } #endif /* We are done, transfer complete */ return 1; } /** * ti_mmchs_intr_cmd_compl - called if a 'command complete' IRQ was received * @sc: pointer to the driver context * @cmd: the command that was sent previously * * * LOCKING: * Called from interrupt context * * RETURNS: * Return value indicates if the transaction is complete, not done = 0, done != 0 */ static int ti_mmchs_intr_cmd_compl(struct ti_mmchs_softc *sc, struct mmc_command *cmd) { uint32_t cmd_reg; /* Copy the response into the request struct ... if a response was * expected */ if (cmd != NULL && (cmd->flags & MMC_RSP_PRESENT)) { if (cmd->flags & MMC_RSP_136) { cmd->resp[3] = ti_mmchs_read_4(sc, MMCHS_RSP10); cmd->resp[2] = ti_mmchs_read_4(sc, MMCHS_RSP32); cmd->resp[1] = ti_mmchs_read_4(sc, MMCHS_RSP54); cmd->resp[0] = ti_mmchs_read_4(sc, MMCHS_RSP76); } else { cmd->resp[0] = ti_mmchs_read_4(sc, MMCHS_RSP10); } } /* Check if the command was expecting some data transfer, if not * we are done. */ cmd_reg = ti_mmchs_read_4(sc, MMCHS_CMD); return ((cmd_reg & MMCHS_CMD_DP) == 0); } /** * ti_mmchs_intr_error - handles error interrupts * @sc: pointer to the driver context * @cmd: the command that was sent previously * @stat_reg: the value that was in the status register * * * LOCKING: * Called from interrupt context * * RETURNS: * Return value indicates if the transaction is complete, not done = 0, done != 0 */ static int ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd, uint32_t stat_reg) { ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg); /* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */ if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND || cmd->opcode == ACMD_SD_SEND_OP_COND)) cmd->error = MMC_ERR_NONE; else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO)) cmd->error = MMC_ERR_TIMEOUT; else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC)) cmd->error = MMC_ERR_BADCRC; else cmd->error = MMC_ERR_FAILED; /* If a dma transaction we should also stop the dma transfer */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) { /* Abort the DMA transfer (DDIR bit tells direction) */ if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR) #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else ti_sdma_stop_xfer(sc->sc_dmach_rd); #endif else #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else ti_sdma_stop_xfer(sc->sc_dmach_wr); #endif /* If an error occure abort the DMA operation and free the dma map */ if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) { bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap); sc->sc_dmamapped--; } } /* Command error occured? ... if so issue a soft reset for the cmd fsm */ if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_CTO)) { ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRC); } /* Data error occured? ... if so issue a soft reset for the data line */ if (stat_reg & (MMCHS_STAT_DEB | MMCHS_STAT_DCRC | MMCHS_STAT_DTO)) { ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRD); } /* On any error the command is cancelled ... so we are done */ return 1; } /** * ti_mmchs_intr - interrupt handler for MMC/SD/SDIO controller * @arg: pointer to the driver context * * Interrupt handler for the MMC/SD/SDIO controller, responsible for handling * the IRQ and clearing the status flags. * * LOCKING: * Called from interrupt context * * RETURNS: * nothing */ static void ti_mmchs_intr(void *arg) { struct ti_mmchs_softc *sc = (struct ti_mmchs_softc *) arg; uint32_t stat_reg; int done = 0; TI_MMCHS_LOCK(sc); stat_reg = ti_mmchs_read_4(sc, MMCHS_STAT) & (ti_mmchs_read_4(sc, MMCHS_IE) | MMCHS_STAT_ERRI); if (sc->curcmd == NULL) { device_printf(sc->sc_dev, "Error: current cmd NULL, already done?\n"); ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); TI_MMCHS_UNLOCK(sc); return; } if (stat_reg & MMCHS_STAT_ERRI) { /* An error has been tripped in the status register */ done = ti_mmchs_intr_error(sc, sc->curcmd, stat_reg); } else { /* NOTE: This implementation could be a bit inefficent, I don't think * it is necessary to handle both the 'command complete' and 'transfer * complete' for data transfers ... presumably just transfer complete * is enough. */ /* No error */ sc->curcmd->error = MMC_ERR_NONE; /* Check if the command completed */ if (stat_reg & MMCHS_STAT_CC) { done = ti_mmchs_intr_cmd_compl(sc, sc->curcmd); } /* Check if the transfer has completed */ if (stat_reg & MMCHS_STAT_TC) { done = ti_mmchs_intr_xfer_compl(sc, sc->curcmd); } } /* Clear all the interrupt status bits by writing the value back */ ti_mmchs_write_4(sc, MMCHS_STAT, stat_reg); /* This may mark the command as done if there is no stop request */ /* TODO: This is a bit ugly, needs fix-up */ if (done) { ti_mmchs_start(sc); } TI_MMCHS_UNLOCK(sc); } #ifdef SOC_TI_AM335X static void ti_mmchs_edma3_rx_xfer_setup(struct ti_mmchs_softc *sc, uint32_t src_paddr, uint32_t dst_paddr, uint16_t blk_size, uint16_t num_blks) { struct ti_edma3cc_param_set ps; bzero(&ps, sizeof(struct ti_edma3cc_param_set)); ps.src = src_paddr; ps.dst = dst_paddr; ps.dstbidx = 4; ps.dstcidx = blk_size; ps.acnt = 4; ps.bcnt = blk_size/4; ps.ccnt = num_blks; ps.link = 0xffff; ps.opt.tcc = sc->dma_rx_trig; ps.opt.tcinten = 1; ps.opt.fwid = 2; /* fifo width is 32 */ ps.opt.sam = 1; ps.opt.syncdim = 1; ti_edma3_param_write(sc->dma_rx_trig, &ps); ti_edma3_enable_transfer_event(sc->dma_rx_trig); } static void ti_mmchs_edma3_tx_xfer_setup(struct ti_mmchs_softc *sc, uint32_t src_paddr, uint32_t dst_paddr, uint16_t blk_size, uint16_t num_blks) { struct ti_edma3cc_param_set ps; bzero(&ps, sizeof(struct ti_edma3cc_param_set)); ps.src = src_paddr; ps.dst = dst_paddr; ps.srccidx = blk_size; ps.bcnt = blk_size/4; ps.ccnt = num_blks; ps.srcbidx = 4; ps.acnt = 0x4; ps.link = 0xffff; ps.opt.tcc = sc->dma_tx_trig; ps.opt.tcinten = 1; ps.opt.fwid = 2; /* fifo width is 32 */ ps.opt.dam = 1; ps.opt.syncdim = 1; ti_edma3_param_write(sc->dma_tx_trig, &ps); ti_edma3_enable_transfer_event(sc->dma_tx_trig); } #endif /** * ti_mmchs_start_cmd - starts the given command * @sc: pointer to the driver context * @cmd: the command to start * * The call tree for this function is * - ti_mmchs_start_cmd * - ti_mmchs_start * - ti_mmchs_request * * LOCKING: * Caller should be holding the OMAP_MMC lock. * * RETURNS: * nothing */ static void ti_mmchs_start_cmd(struct ti_mmchs_softc *sc, struct mmc_command *cmd) { uint32_t cmd_reg, con_reg, ise_reg; struct mmc_data *data; struct mmc_request *req; void *vaddr; bus_addr_t paddr; #ifndef SOC_TI_AM335X uint32_t pktsize; #endif sc->curcmd = cmd; data = cmd->data; req = cmd->mrq; /* Ensure the STR and MIT bits are cleared, these are only used for special * command types. */ con_reg = ti_mmchs_read_4(sc, MMCHS_CON); con_reg &= ~(MMCHS_CON_STR | MMCHS_CON_MIT); /* Load the command into bits 29:24 of the CMD register */ cmd_reg = (uint32_t)(cmd->opcode & 0x3F) << 24; /* Set the default set of interrupts */ ise_reg = (MMCHS_STAT_CERR | MMCHS_STAT_CTO | MMCHS_STAT_CC | MMCHS_STAT_CEB); /* Enable CRC checking if requested */ if (cmd->flags & MMC_RSP_CRC) ise_reg |= MMCHS_STAT_CCRC; /* Enable reply index checking if the response supports it */ if (cmd->flags & MMC_RSP_OPCODE) ise_reg |= MMCHS_STAT_CIE; /* Set the expected response length */ if (MMC_RSP(cmd->flags) == MMC_RSP_NONE) { cmd_reg |= MMCHS_CMD_RSP_TYPE_NO; } else { if (cmd->flags & MMC_RSP_136) cmd_reg |= MMCHS_CMD_RSP_TYPE_136; else if (cmd->flags & MMC_RSP_BUSY) cmd_reg |= MMCHS_CMD_RSP_TYPE_48_BSY; else cmd_reg |= MMCHS_CMD_RSP_TYPE_48; /* Enable command index/crc checks if necessary expected */ if (cmd->flags & MMC_RSP_CRC) cmd_reg |= MMCHS_CMD_CCCE; if (cmd->flags & MMC_RSP_OPCODE) cmd_reg |= MMCHS_CMD_CICE; } /* Set the bits for the special commands CMD12 (MMC_STOP_TRANSMISSION) and * CMD52 (SD_IO_RW_DIRECT) */ if (cmd->opcode == MMC_STOP_TRANSMISSION) cmd_reg |= MMCHS_CMD_CMD_TYPE_IO_ABORT; /* Check if there is any data to write */ if (data == NULL) { /* Clear the block count */ ti_mmchs_write_4(sc, MMCHS_BLK, 0); /* The no data case is fairly simple */ ti_mmchs_write_4(sc, MMCHS_CON, con_reg); ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); return; } /* Indicate that data is present */ cmd_reg |= MMCHS_CMD_DP | MMCHS_CMD_MSBS | MMCHS_CMD_BCE; /* Indicate a read operation */ if (data->flags & MMC_DATA_READ) cmd_reg |= MMCHS_CMD_DDIR; /* Streaming mode */ if (data->flags & MMC_DATA_STREAM) { con_reg |= MMCHS_CON_STR; } /* Multi-block mode */ if (data->flags & MMC_DATA_MULTI) { cmd_reg |= MMCHS_CMD_MSBS; } /* Enable extra interrupt sources for the transfer */ ise_reg |= (MMCHS_STAT_TC | MMCHS_STAT_DTO | MMCHS_STAT_DEB | MMCHS_STAT_CEB); if (cmd->flags & MMC_RSP_CRC) ise_reg |= MMCHS_STAT_DCRC; /* Enable the DMA transfer bit */ cmd_reg |= MMCHS_CMD_DE; /* Set the block size and block count */ ti_mmchs_write_4(sc, MMCHS_BLK, (1 << 16) | data->len); /* Setup the DMA stuff */ if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) { vaddr = data->data; data->xfer_len = 0; /* Map the buffer buf into bus space using the dmamap map. */ if (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, vaddr, data->len, ti_mmchs_getaddr, &paddr, 0) != 0) { if (req->cmd->flags & STOP_STARTED) req->stop->error = MMC_ERR_NO_MEMORY; else req->cmd->error = MMC_ERR_NO_MEMORY; sc->req = NULL; sc->curcmd = NULL; req->done(req); return; } #ifndef SOC_TI_AM335X /* Calculate the packet size, the max packet size is 512 bytes * (or 128 32-bit elements). */ pktsize = min((data->len / 4), (512 / 4)); #endif /* Sync the DMA buffer and setup the DMA controller */ if (data->flags & MMC_DATA_READ) { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREREAD); #ifdef SOC_TI_AM335X ti_mmchs_edma3_rx_xfer_setup(sc, sc->sc_data_reg_paddr, paddr, data->len, 1); #else ti_sdma_start_xfer_packet(sc->sc_dmach_rd, sc->sc_data_reg_paddr, paddr, 1, (data->len / 4), pktsize); #endif } else { bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREWRITE); #ifdef SOC_TI_AM335X ti_mmchs_edma3_tx_xfer_setup(sc, paddr, sc->sc_data_reg_paddr, data->len, 1); #else ti_sdma_start_xfer_packet(sc->sc_dmach_wr, paddr, sc->sc_data_reg_paddr, 1, (data->len / 4), pktsize); #endif } /* Increase the mapped count */ sc->sc_dmamapped++; sc->sc_cmd_data_vaddr = vaddr; sc->sc_cmd_data_len = data->len; } /* Finally kick off the command */ ti_mmchs_write_4(sc, MMCHS_CON, con_reg); ti_mmchs_write_4(sc, MMCHS_IE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg); ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg); ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg); /* and we're done */ } /** * ti_mmchs_start - starts a request stored in the driver context * @sc: pointer to the driver context * * This function is called by ti_mmchs_request() in response to a read/write * request from the MMC core module. * * LOCKING: * Caller should be holding the OMAP_MMC lock. * * RETURNS: * nothing */ static void ti_mmchs_start(struct ti_mmchs_softc *sc) { struct mmc_request *req; /* Sanity check we have a request */ req = sc->req; if (req == NULL) return; /* assert locked */ if (!(sc->flags & CMD_STARTED)) { sc->flags |= CMD_STARTED; ti_mmchs_start_cmd(sc, req->cmd); return; } if (!(sc->flags & STOP_STARTED) && req->stop) { sc->flags |= STOP_STARTED; ti_mmchs_start_cmd(sc, req->stop); return; } /* We must be done -- bad idea to do this while locked? */ sc->req = NULL; sc->curcmd = NULL; req->done(req); } /** * ti_mmchs_request - entry point for all read/write/cmd requests * @brdev: mmc bridge device handle * @reqdev: the device doing the requesting ? * @req: the action requested * * LOCKING: * None, internally takes the OMAP_MMC lock. * * RETURNS: * 0 on success * EBUSY if the driver is already performing a request */ static int ti_mmchs_request(device_t brdev, device_t reqdev, struct mmc_request *req) { struct ti_mmchs_softc *sc = device_get_softc(brdev); TI_MMCHS_LOCK(sc); /* * XXX do we want to be able to queue up multiple commands? * XXX sounds like a good idea, but all protocols are sync, so * XXX maybe the idea is naive... */ if (sc->req != NULL) { TI_MMCHS_UNLOCK(sc); return (EBUSY); } /* Store the request and start the command */ sc->req = req; sc->flags = 0; ti_mmchs_start(sc); TI_MMCHS_UNLOCK(sc); return (0); } /** * ti_mmchs_get_ro - returns the status of the read-only setting * @brdev: mmc bridge device handle * @reqdev: device doing the request * * This function is relies on hint'ed values to determine which GPIO is used * to determine if the write protect is enabled. On the BeagleBoard the pin * is GPIO_23. * * LOCKING: * - * * RETURNS: * 0 if not read-only * 1 if read only */ static int ti_mmchs_get_ro(device_t brdev, device_t reqdev) { struct ti_mmchs_softc *sc = device_get_softc(brdev); unsigned int readonly = 0; TI_MMCHS_LOCK(sc); if ((sc->sc_wp_gpio_pin != -1) && (sc->sc_gpio_dev != NULL)) { if (GPIO_PIN_GET(sc->sc_gpio_dev, sc->sc_wp_gpio_pin, &readonly) != 0) readonly = 0; else readonly = (readonly == 0) ? 0 : 1; } TI_MMCHS_UNLOCK(sc); return (readonly); } /** * ti_mmchs_send_init_stream - sets bus/controller settings * @brdev: mmc bridge device handle * @reqdev: device doing the request * * Send init stream sequence to card before sending IDLE command * * LOCKING: * * * RETURNS: * 0 if function succeeded */ static void ti_mmchs_send_init_stream(struct ti_mmchs_softc *sc) { unsigned long timeout; uint32_t ie, ise, con; ti_mmchs_dbg(sc, "Performing init sequence\n"); /* Prior to issuing any command, the MMCHS controller has to execute a * special INIT procedure. The MMCHS controller has to generate a clock * during 1ms. During the INIT procedure, the MMCHS controller generates 80 * clock periods. In order to keep the 1ms gap, the MMCHS controller should * be configured to generate a clock whose frequency is smaller or equal to * 80 KHz. If the MMCHS controller divider bitfield width doesn't allow to * choose big values, the MMCHS controller driver should perform the INIT * procedure twice or three times. Twice is generally enough. * * The INIt procedure is executed by setting MMCHS1.MMCHS_CON[1] INIT * bitfield to 1 and by sending a dummy command, writing 0x00000000 in * MMCHS1.MMCHS_CMD register. */ /* Disable interrupt status events but enable interrupt generation. * This doesn't seem right to me, but if the interrupt generation is not * enabled the CC bit doesn't seem to be set in the STAT register. */ /* Enable interrupt generation */ ie = ti_mmchs_read_4(sc, MMCHS_IE); ti_mmchs_write_4(sc, MMCHS_IE, 0x307F0033); /* Disable generation of status events (stops interrupt triggering) */ ise = ti_mmchs_read_4(sc, MMCHS_ISE); ti_mmchs_write_4(sc, MMCHS_ISE, 0); /* Set the initialise stream bit */ con = ti_mmchs_read_4(sc, MMCHS_CON); con |= MMCHS_CON_INIT; ti_mmchs_write_4(sc, MMCHS_CON, con); /* Write a dummy command 0x00 */ ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); /* Loop waiting for the command to finish */ timeout = hz; do { pause("MMCINIT", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "Error: first stream init timed out\n"); break; } } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); /* Clear the command complete status bit */ ti_mmchs_write_4(sc, MMCHS_STAT, MMCHS_STAT_CC); /* Write another dummy command 0x00 */ ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000); /* Loop waiting for the second command to finish */ timeout = hz; do { pause("MMCINIT", 1); if (timeout-- == 0) { device_printf(sc->sc_dev, "Error: second stream init timed out\n"); break; } } while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC)); /* Clear the stream init bit */ con &= ~MMCHS_CON_INIT; ti_mmchs_write_4(sc, MMCHS_CON, con); /* Clear the status register, then restore the IE and ISE registers */ ti_mmchs_write_4(sc, MMCHS_STAT, 0xffffffff); ti_mmchs_read_4(sc, MMCHS_STAT); ti_mmchs_write_4(sc, MMCHS_ISE, ise); ti_mmchs_write_4(sc, MMCHS_IE, ie); } /** * ti_mmchs_update_ios - sets bus/controller settings * @brdev: mmc bridge device handle * @reqdev: device doing the request * * Called to set the bus and controller settings that need to be applied to * the actual HW. Currently this function just sets the bus width and the * clock speed. * * LOCKING: * * * RETURNS: * 0 if function succeeded */ static int ti_mmchs_update_ios(device_t brdev, device_t reqdev) { struct ti_mmchs_softc *sc; struct mmc_host *host; struct mmc_ios *ios; uint32_t clkdiv; uint32_t hctl_reg; uint32_t con_reg; uint32_t sysctl_reg; #ifndef SOC_TI_AM335X uint16_t mv; #endif unsigned long timeout; int do_card_init = 0; sc = device_get_softc(brdev); host = &sc->host; ios = &host->ios; /* Read the initial values of the registers */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); con_reg = ti_mmchs_read_4(sc, MMCHS_CON); /* Set the bus width */ switch (ios->bus_width) { case bus_width_1: hctl_reg &= ~MMCHS_HCTL_DTW; con_reg &= ~MMCHS_CON_DW8; break; case bus_width_4: hctl_reg |= MMCHS_HCTL_DTW; con_reg &= ~MMCHS_CON_DW8; break; case bus_width_8: con_reg |= MMCHS_CON_DW8; break; } /* Finally write all these settings back to the registers */ ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); ti_mmchs_write_4(sc, MMCHS_CON, con_reg); /* Check if we need to change the external voltage regulator */ if (sc->sc_cur_power_mode != ios->power_mode) { if (ios->power_mode == power_up) { /* Set the power level */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); hctl_reg &= ~(MMCHS_HCTL_SDVS_MASK | MMCHS_HCTL_SDBP); if ((ios->vdd == -1) || (ios->vdd >= vdd_240)) { #ifndef SOC_TI_AM335X mv = 3000; #endif hctl_reg |= MMCHS_HCTL_SDVS_V30; } else { #ifndef SOC_TI_AM335X mv = 1800; #endif hctl_reg |= MMCHS_HCTL_SDVS_V18; } ti_mmchs_write_4(sc, MMCHS_HCTL, hctl_reg); #ifdef SOC_TI_AM335X printf("%s: TWL unimplemented\n", __func__); #else /* Set the desired voltage on the regulator */ if (sc->sc_vreg_dev && sc->sc_vreg_name) twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, mv); #endif /* Enable the bus power */ ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg | MMCHS_HCTL_SDBP)); timeout = hz; while (!(ti_mmchs_read_4(sc, MMCHS_HCTL) & MMCHS_HCTL_SDBP)) { if (timeout-- == 0) break; pause("MMC_PWRON", 1); } } else if (ios->power_mode == power_off) { /* Disable the bus power */ hctl_reg = ti_mmchs_read_4(sc, MMCHS_HCTL); ti_mmchs_write_4(sc, MMCHS_HCTL, (hctl_reg & ~MMCHS_HCTL_SDBP)); #ifdef SOC_TI_AM335X printf("%s: TWL unimplemented\n", __func__); #else /* Turn the power off on the voltage regulator */ if (sc->sc_vreg_dev && sc->sc_vreg_name) twl_vreg_set_voltage(sc->sc_vreg_dev, sc->sc_vreg_name, 0); #endif } else if (ios->power_mode == power_on) { /* Force a card re-initialisation sequence */ do_card_init = 1; } /* Save the new power state */ sc->sc_cur_power_mode = ios->power_mode; } /* need the MMCHS_SYSCTL register */ sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL); /* Just in case this hasn't been setup before, set the timeout to the default */ sysctl_reg &= ~MMCHS_SYSCTL_DTO_MASK; sysctl_reg |= MMCHS_SYSCTL_DTO(0xe); /* Disable the clock output while configuring the new clock */ sysctl_reg &= ~(MMCHS_SYSCTL_ICE | MMCHS_SYSCTL_CEN); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* bus mode? */ if (ios->clock == 0) { clkdiv = 0; } else { clkdiv = sc->sc_ref_freq / ios->clock; if (clkdiv < 1) clkdiv = 1; if ((sc->sc_ref_freq / clkdiv) > ios->clock) clkdiv += 1; if (clkdiv > 250) clkdiv = 250; } /* Set the new clock divider */ sysctl_reg &= ~MMCHS_SYSCTL_CLKD_MASK; sysctl_reg |= MMCHS_SYSCTL_CLKD(clkdiv); /* Write the new settings ... */ ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* ... write the internal clock enable bit ... */ ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg | MMCHS_SYSCTL_ICE); /* ... wait for the clock to stablise ... */ while (((sysctl_reg = ti_mmchs_read_4(sc, MMCHS_SYSCTL)) & MMCHS_SYSCTL_ICS) == 0) { continue; } /* ... then enable */ sysctl_reg |= MMCHS_SYSCTL_CEN; ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl_reg); /* If the power state has changed to 'power_on' then run the init sequence*/ if (do_card_init) { ti_mmchs_send_init_stream(sc); } /* Set the bus mode (opendrain or normal) */ con_reg = ti_mmchs_read_4(sc, MMCHS_CON); if (ios->bus_mode == opendrain) con_reg |= MMCHS_CON_OD; else con_reg &= ~MMCHS_CON_OD; ti_mmchs_write_4(sc, MMCHS_CON, con_reg); return (0); } /** * ti_mmchs_acquire_host - * @brdev: mmc bridge device handle * @reqdev: device doing the request * * TODO: Is this function needed ? * * LOCKING: * none * * RETURNS: * 0 function succeeded * */ static int ti_mmchs_acquire_host(device_t brdev, device_t reqdev) { struct ti_mmchs_softc *sc = device_get_softc(brdev); int err = 0; TI_MMCHS_LOCK(sc); while (sc->bus_busy) { msleep(sc, &sc->sc_mtx, PZERO, "mmc", hz / 5); } sc->bus_busy++; TI_MMCHS_UNLOCK(sc); return (err); } /** * ti_mmchs_release_host - * @brdev: mmc bridge device handle * @reqdev: device doing the request * * TODO: Is this function needed ? * * LOCKING: * none * * RETURNS: * 0 function succeeded * */ static int ti_mmchs_release_host(device_t brdev, device_t reqdev) { struct ti_mmchs_softc *sc = device_get_softc(brdev); TI_MMCHS_LOCK(sc); sc->bus_busy--; wakeup(sc); TI_MMCHS_UNLOCK(sc); return (0); } /** * ti_mmchs_read_ivar - returns driver conf variables * @bus: * @child: * @which: The variable to get the result for * @result: Upon return will store the variable value * * * * LOCKING: * None, caller must hold locks * * RETURNS: * 0 on success * EINVAL if the variable requested is invalid */ static int ti_mmchs_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct ti_mmchs_softc *sc = device_get_softc(bus); switch (which) { case MMCBR_IVAR_BUS_MODE: *(int *)result = sc->host.ios.bus_mode; break; case MMCBR_IVAR_BUS_WIDTH: *(int *)result = sc->host.ios.bus_width; break; case MMCBR_IVAR_CHIP_SELECT: *(int *)result = sc->host.ios.chip_select; break; case MMCBR_IVAR_CLOCK: *(int *)result = sc->host.ios.clock; break; case MMCBR_IVAR_F_MIN: *(int *)result = sc->host.f_min; break; case MMCBR_IVAR_F_MAX: *(int *)result = sc->host.f_max; break; case MMCBR_IVAR_HOST_OCR: *(int *)result = sc->host.host_ocr; break; case MMCBR_IVAR_MODE: *(int *)result = sc->host.mode; break; case MMCBR_IVAR_OCR: *(int *)result = sc->host.ocr; break; case MMCBR_IVAR_POWER_MODE: *(int *)result = sc->host.ios.power_mode; break; case MMCBR_IVAR_VDD: *(int *)result = sc->host.ios.vdd; break; case MMCBR_IVAR_CAPS: *(int *)result = sc->host.caps; break; case MMCBR_IVAR_MAX_DATA: *(int *)result = 1; break; default: return (EINVAL); } return (0); } /** * ti_mmchs_write_ivar - writes a driver conf variables * @bus: * @child: * @which: The variable to set * @value: The value to write into the variable * * * * LOCKING: * None, caller must hold locks * * RETURNS: * 0 on success * EINVAL if the variable requested is invalid */ static int ti_mmchs_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct ti_mmchs_softc *sc = device_get_softc(bus); switch (which) { case MMCBR_IVAR_BUS_MODE: sc->host.ios.bus_mode = value; break; case MMCBR_IVAR_BUS_WIDTH: sc->host.ios.bus_width = value; break; case MMCBR_IVAR_CHIP_SELECT: sc->host.ios.chip_select = value; break; case MMCBR_IVAR_CLOCK: sc->host.ios.clock = value; break; case MMCBR_IVAR_MODE: sc->host.mode = value; break; case MMCBR_IVAR_OCR: sc->host.ocr = value; break; case MMCBR_IVAR_POWER_MODE: sc->host.ios.power_mode = value; break; case MMCBR_IVAR_VDD: sc->host.ios.vdd = value; break; /* These are read-only */ case MMCBR_IVAR_CAPS: case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_F_MIN: case MMCBR_IVAR_F_MAX: case MMCBR_IVAR_MAX_DATA: return (EINVAL); default: return (EINVAL); } return (0); } /** * ti_mmchs_hw_init - initialises the MMC/SD/SIO controller * @dev: mmc device handle * * Called by the driver attach function during driver initialisation. This * function is responsibly to setup the controller ready for transactions. * * LOCKING: * No locking, assumed to only be called during initialisation. * * RETURNS: * nothing */ static void ti_mmchs_hw_init(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); clk_ident_t clk; unsigned long timeout; uint32_t sysctl; uint32_t capa; uint32_t con, sysconfig; /* 1: Enable the controller and interface/functional clocks */ clk = MMC0_CLK + sc->device_id; if (ti_prcm_clk_enable(clk) != 0) { device_printf(dev, "Error: failed to enable MMC clock\n"); return; } /* 1a: Get the frequency of the source clock */ if (ti_prcm_clk_get_source_freq(clk, &sc->sc_ref_freq) != 0) { device_printf(dev, "Error: failed to get source clock freq\n"); return; } /* 2: Issue a softreset to the controller */ sysconfig = ti_mmchs_read_4(sc, MMCHS_SYSCONFIG); sysconfig |= MMCHS_SYSCONFIG_SRST; ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, sysconfig); timeout = 100; while ((ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & 0x01) == 0x0) { DELAY(1000); if (timeout-- == 0) { device_printf(dev, "Error: reset operation timed out\n"); return; } } /* 3: Reset both the command and data state machines */ sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL); ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | MMCHS_SYSCTL_SRA); timeout = 100; while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & MMCHS_SYSCTL_SRA) != 0x0) { DELAY(1000); if (timeout-- == 0) { device_printf(dev, "Error: reset operation timed out\n"); return; } } /* 4: Set initial host configuration (1-bit mode, pwroff) and capabilities */ ti_mmchs_write_4(sc, MMCHS_HCTL, MMCHS_HCTL_SDVS_V30); capa = ti_mmchs_read_4(sc, MMCHS_CAPA); ti_mmchs_write_4(sc, MMCHS_CAPA, capa | MMCHS_CAPA_VS30 | MMCHS_CAPA_VS18); /* 5: Set the initial bus configuration * 0 CTPL_MMC_SD : Control Power for DAT1 line * 0 WPP_ACTIVE_HIGH : Write protect polarity * 0 CDP_ACTIVE_HIGH : Card detect polarity * 0 CTO_ENABLED : MMC interrupt command * 0 DW8_DISABLED : 8-bit mode MMC select * 0 MODE_FUNC : Mode select * 0 STREAM_DISABLED : Stream command * 0 HR_DISABLED : Broadcast host response * 0 INIT_DISABLED : Send initialization stream * 0 OD_DISABLED : No Open Drain */ con = ti_mmchs_read_4(sc, MMCHS_CON) & MMCHS_CON_DVAL_MASK; ti_mmchs_write_4(sc, MMCHS_CON, con); } /** * ti_mmchs_fini - shutdown the MMC/SD/SIO controller * @dev: mmc device handle * * Responsible for shutting done the MMC controller, this function may be * called as part of a reset sequence. * * LOCKING: * No locking, assumed to be called during tear-down/reset. * * RETURNS: * nothing */ static void ti_mmchs_hw_fini(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); /* Disable all interrupts */ ti_mmchs_write_4(sc, MMCHS_ISE, 0x00000000); ti_mmchs_write_4(sc, MMCHS_IE, 0x00000000); /* Disable the functional and interface clocks */ ti_prcm_clk_disable(MMC0_CLK + sc->device_id); } /** * ti_mmchs_init_dma_channels - initalise the DMA channels * @sc: driver soft context * * Attempts to activate an RX and TX DMA channel for the MMC device. * * LOCKING: * No locking, assumed to be called during tear-down/reset. * * RETURNS: * 0 on success, a negative error code on failure. */ static int ti_mmchs_init_dma_channels(struct ti_mmchs_softc *sc) { #ifdef SOC_TI_AM335X switch (sc->device_id) { case 0: sc->dma_tx_trig = TI_EDMA3_EVENT_SDTXEVT0; sc->dma_rx_trig = TI_EDMA3_EVENT_SDRXEVT0; break; case 1: sc->dma_tx_trig = TI_EDMA3_EVENT_SDTXEVT1; sc->dma_rx_trig = TI_EDMA3_EVENT_SDRXEVT1; break; default: return(EINVAL); } #define EVTQNUM 0 /* TODO EDMA3 have 3 queues, so we need some queue allocation call */ ti_edma3_init(EVTQNUM); ti_edma3_request_dma_ch(sc->dma_tx_trig, sc->dma_tx_trig, EVTQNUM); ti_edma3_request_dma_ch(sc->dma_rx_trig, sc->dma_rx_trig, EVTQNUM); #else int err; uint32_t rev; /* Get the current chip revision */ rev = ti_revision(); if ((OMAP_REV_DEVICE(rev) != OMAP4430_DEV) && (sc->device_id > 3)) return(EINVAL); /* Get the DMA MMC triggers */ switch (sc->device_id) { case 1: sc->dma_tx_trig = 60; sc->dma_rx_trig = 61; break; case 2: sc->dma_tx_trig = 46; sc->dma_rx_trig = 47; break; case 3: sc->dma_tx_trig = 76; sc->dma_rx_trig = 77; break; /* The following are OMAP4 only */ case 4: sc->dma_tx_trig = 56; sc->dma_rx_trig = 57; break; case 5: sc->dma_tx_trig = 58; sc->dma_rx_trig = 59; break; default: return(EINVAL); } /* Activate a RX channel from the OMAP DMA driver */ err = ti_sdma_activate_channel(&sc->sc_dmach_rd, ti_mmchs_dma_intr, sc); if (err != 0) return(err); /* Setup the RX channel for MMC data transfers */ ti_sdma_set_xfer_burst(sc->sc_dmach_rd, TI_SDMA_BURST_NONE, TI_SDMA_BURST_64); ti_sdma_set_xfer_data_type(sc->sc_dmach_rd, TI_SDMA_DATA_32BITS_SCALAR); ti_sdma_sync_params(sc->sc_dmach_rd, sc->dma_rx_trig, TI_SDMA_SYNC_PACKET | TI_SDMA_SYNC_TRIG_ON_SRC); ti_sdma_set_addr_mode(sc->sc_dmach_rd, TI_SDMA_ADDR_CONSTANT, TI_SDMA_ADDR_POST_INCREMENT); /* Activate and configure the TX DMA channel */ err = ti_sdma_activate_channel(&sc->sc_dmach_wr, ti_mmchs_dma_intr, sc); if (err != 0) return(err); /* Setup the TX channel for MMC data transfers */ ti_sdma_set_xfer_burst(sc->sc_dmach_wr, TI_SDMA_BURST_64, TI_SDMA_BURST_NONE); ti_sdma_set_xfer_data_type(sc->sc_dmach_wr, TI_SDMA_DATA_32BITS_SCALAR); ti_sdma_sync_params(sc->sc_dmach_wr, sc->dma_tx_trig, TI_SDMA_SYNC_PACKET | TI_SDMA_SYNC_TRIG_ON_DST); ti_sdma_set_addr_mode(sc->sc_dmach_wr, TI_SDMA_ADDR_POST_INCREMENT, TI_SDMA_ADDR_CONSTANT); #endif return(0); } /** * ti_mmchs_deactivate - deactivates the driver * @dev: mmc device handle * * Unmaps the register set and releases the IRQ resource. * * LOCKING: * None required * * RETURNS: * nothing */ static void ti_mmchs_deactivate(device_t dev) { struct ti_mmchs_softc *sc= device_get_softc(dev); /* Remove the IRQ handler */ if (sc->sc_irq_h != NULL) { bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_h); sc->sc_irq_h = NULL; } /* Do the generic detach */ bus_generic_detach(sc->sc_dev); #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else /* Deactivate the DMA channels */ ti_sdma_deactivate_channel(sc->sc_dmach_rd); ti_sdma_deactivate_channel(sc->sc_dmach_wr); #endif /* Unmap the MMC controller registers */ if (sc->sc_mem_res != 0) { bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_irq_res), sc->sc_mem_res); sc->sc_mem_res = NULL; } /* Release the IRQ resource */ if (sc->sc_irq_res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); sc->sc_irq_res = NULL; } return; } /** * ti_mmchs_activate - activates the driver * @dev: mmc device handle * * Maps in the register set and requests an IRQ handler for the MMC controller. * * LOCKING: * None required * * RETURNS: * 0 on sucess * ENOMEM if failed to map register set */ static int ti_mmchs_activate(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); int rid; int err; /* Get the memory resource for the register mapping */ rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) panic("%s: Cannot map registers", device_get_name(dev)); /* Allocate an IRQ resource for the MMC controller */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) goto errout; /* Allocate DMA tags and maps */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MAXPHYS, 1, MAXPHYS, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dmatag); if (err != 0) goto errout; err = bus_dmamap_create(sc->sc_dmatag, 0, &sc->sc_dmamap); if (err != 0) goto errout; /* Initialise the DMA channels to be used by the controller */ err = ti_mmchs_init_dma_channels(sc); if (err != 0) goto errout; /* Set the register offset */ - if (ti_chip() == CHIP_OMAP_3) - sc->sc_reg_off = OMAP3_MMCHS_REG_OFFSET; - else if (ti_chip() == CHIP_OMAP_4) + if (ti_chip() == CHIP_OMAP_4) sc->sc_reg_off = OMAP4_MMCHS_REG_OFFSET; else if (ti_chip() == CHIP_AM335X) sc->sc_reg_off = AM335X_MMCHS_REG_OFFSET; else panic("Unknown OMAP device\n"); /* Get the physical address of the MMC data register, needed for DMA */ sc->sc_data_reg_paddr = BUS_SPACE_PHYSADDR(sc->sc_mem_res, sc->sc_reg_off + MMCHS_DATA); /* Set the initial power state to off */ sc->sc_cur_power_mode = power_off; return (0); errout: ti_mmchs_deactivate(dev); return (ENOMEM); } /** * ti_mmchs_probe - probe function for the driver * @dev: mmc device handle * * * * RETURNS: * always returns 0 */ static int ti_mmchs_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,mmchs")) return (ENXIO); device_set_desc(dev, "TI MMC/SD/SDIO High Speed Interface"); return (0); } /** * ti_mmchs_attach - attach function for the driver * @dev: mmc device handle * * Driver initialisation, sets-up the bus mappings, DMA mapping/channels and * the actual controller by calling ti_mmchs_init(). * * RETURNS: * Returns 0 on success or a negative error code. */ static int ti_mmchs_attach(device_t dev) { struct ti_mmchs_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); phandle_t node; pcell_t did; int err; /* Save the device and bus tag */ sc->sc_dev = dev; /* Get the mmchs device id from FDT */ node = ofw_bus_get_node(dev); if ((OF_getprop(node, "mmchs-device-id", &did, sizeof(did))) <= 0) { device_printf(dev, "missing mmchs-device-id attribute in FDT\n"); return (ENXIO); } sc->device_id = fdt32_to_cpu(did); /* Initiate the mtex lock */ TI_MMCHS_LOCK_INIT(sc); /* Indicate the DMA channels haven't yet been allocated */ sc->sc_dmach_rd = (unsigned int)-1; sc->sc_dmach_wr = (unsigned int)-1; /* Get the hint'ed write detect pin */ /* TODO: take this from FDT */ if (resource_int_value("ti_mmchs", unit, "wp_gpio", &sc->sc_wp_gpio_pin) != 0){ sc->sc_wp_gpio_pin = -1; } else { /* Get the GPIO device, we need this for the write protect pin */ sc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc->sc_gpio_dev == NULL) device_printf(dev, "Error: failed to get the GPIO device\n"); else GPIO_PIN_SETFLAGS(sc->sc_gpio_dev, sc->sc_wp_gpio_pin, GPIO_PIN_INPUT); } /* Get the TWL voltage regulator device, we need this to for setting the * voltage of the bus on certain OMAP platforms. */ sc->sc_vreg_name = NULL; /* TODO: add voltage regulator knob to FDT */ #ifdef notyet sc->sc_vreg_dev = devclass_get_device(devclass_find("twl_vreg"), 0); if (sc->sc_vreg_dev == NULL) { device_printf(dev, "Error: failed to get the votlage regulator" " device\n"); sc->sc_vreg_name = NULL; } #endif /* Activate the device */ err = ti_mmchs_activate(dev); if (err) goto out; /* Initialise the controller */ ti_mmchs_hw_init(dev); /* Activate the interrupt and attach a handler */ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_mmchs_intr, sc, &sc->sc_irq_h); if (err != 0) goto out; /* Add host details */ sc->host.f_min = sc->sc_ref_freq / 1023; sc->host.f_max = sc->sc_ref_freq; sc->host.host_ocr = MMC_OCR_290_300 | MMC_OCR_300_310; sc->host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; device_add_child(dev, "mmc", 0); err = bus_generic_attach(dev); out: if (err) { TI_MMCHS_LOCK_DESTROY(sc); ti_mmchs_deactivate(dev); #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else if (sc->sc_dmach_rd != (unsigned int)-1) ti_sdma_deactivate_channel(sc->sc_dmach_rd); if (sc->sc_dmach_wr != (unsigned int)-1) ti_sdma_deactivate_channel(sc->sc_dmach_wr); #endif } return (err); } /** * ti_mmchs_detach - dettach function for the driver * @dev: mmc device handle * * Shutdowns the controll and release resources allocated by the driver. * * RETURNS: * Always returns 0. */ static int ti_mmchs_detach(device_t dev) { #ifndef SOC_TI_AM335X struct ti_mmchs_softc *sc = device_get_softc(dev); #endif ti_mmchs_hw_fini(dev); ti_mmchs_deactivate(dev); #ifdef SOC_TI_AM335X printf("%s: DMA unimplemented\n", __func__); #else ti_sdma_deactivate_channel(sc->sc_dmach_wr); ti_sdma_deactivate_channel(sc->sc_dmach_rd); #endif return (0); } static device_method_t ti_mmchs_methods[] = { /* device_if */ DEVMETHOD(device_probe, ti_mmchs_probe), DEVMETHOD(device_attach, ti_mmchs_attach), DEVMETHOD(device_detach, ti_mmchs_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, ti_mmchs_read_ivar), DEVMETHOD(bus_write_ivar, ti_mmchs_write_ivar), /* mmcbr_if - MMC state machine callbacks */ DEVMETHOD(mmcbr_update_ios, ti_mmchs_update_ios), DEVMETHOD(mmcbr_request, ti_mmchs_request), DEVMETHOD(mmcbr_get_ro, ti_mmchs_get_ro), DEVMETHOD(mmcbr_acquire_host, ti_mmchs_acquire_host), DEVMETHOD(mmcbr_release_host, ti_mmchs_release_host), {0, 0}, }; static driver_t ti_mmchs_driver = { "ti_mmchs", ti_mmchs_methods, sizeof(struct ti_mmchs_softc), }; static devclass_t ti_mmchs_devclass; DRIVER_MODULE(ti_mmchs, simplebus, ti_mmchs_driver, ti_mmchs_devclass, 0, 0); MODULE_DEPEND(ti_mmchs, ti_prcm, 1, 1, 1); #ifdef SOC_TI_AM335X MODULE_DEPEND(ti_mmchs, ti_edma, 1, 1, 1); #else MODULE_DEPEND(ti_mmchs, ti_sdma, 1, 1, 1); #endif MODULE_DEPEND(ti_mmchs, ti_gpio, 1, 1, 1); /* FIXME: MODULE_DEPEND(ti_mmchs, twl_vreg, 1, 1, 1); */ Index: head/sys/arm/ti/ti_sdhci.c =================================================================== --- head/sys/arm/ti/ti_sdhci.c (revision 273040) +++ head/sys/arm/ti/ti_sdhci.c (revision 273041) @@ -1,680 +1,678 @@ /*- * Copyright (c) 2013 Ian Lepore * Copyright (c) 2011 Ben Gray . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdhci_if.h" #include #include #include "gpio_if.h" struct ti_sdhci_softc { device_t dev; device_t gpio_dev; struct resource * mem_res; struct resource * irq_res; void * intr_cookie; struct sdhci_slot slot; uint32_t mmchs_device_id; uint32_t mmchs_reg_off; uint32_t sdhci_reg_off; uint32_t baseclk_hz; uint32_t wp_gpio_pin; uint32_t cmd_and_mode; uint32_t sdhci_clkdiv; boolean_t disable_highspeed; boolean_t force_card_present; }; /* * Table of supported FDT compat strings. * * Note that "ti,mmchs" is our own invention, and should be phased out in favor * of the documented names. * * Note that vendor Beaglebone dtsi files use "ti,omap3-hsmmc" for the am335x. */ static struct ofw_compat_data compat_data[] = { {"ti,omap3-hsmmc", 1}, {"ti,omap4-hsmmc", 1}, {"ti,mmchs", 1}, {NULL, 0}, }; /* * The MMCHS hardware has a few control and status registers at the beginning of * the device's memory map, followed by the standard sdhci register block. * Different SoCs have the register blocks at different offsets from the * beginning of the device. Define some constants to map out the registers we * access, and the various per-SoC offsets. The SDHCI_REG_OFFSET is how far * beyond the MMCHS block the SDHCI block is found; it's the same on all SoCs. */ #define OMAP3_MMCHS_REG_OFFSET 0x000 #define OMAP4_MMCHS_REG_OFFSET 0x100 #define AM335X_MMCHS_REG_OFFSET 0x100 #define SDHCI_REG_OFFSET 0x100 #define MMCHS_SYSCONFIG 0x010 #define MMCHS_SYSCONFIG_RESET (1 << 1) #define MMCHS_SYSSTATUS 0x014 #define MMCHS_SYSSTATUS_RESETDONE (1 << 0) #define MMCHS_CON 0x02C #define MMCHS_CON_DW8 (1 << 5) #define MMCHS_CON_DVAL_8_4MS (3 << 9) #define MMCHS_SYSCTL 0x12C #define MMCHS_SYSCTL_CLKD_MASK 0x3FF #define MMCHS_SYSCTL_CLKD_SHIFT 6 #define MMCHS_SD_CAPA 0x140 #define MMCHS_SD_CAPA_VS18 (1 << 26) #define MMCHS_SD_CAPA_VS30 (1 << 25) #define MMCHS_SD_CAPA_VS33 (1 << 24) static inline uint32_t ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off + sc->mmchs_reg_off)); } static inline void ti_mmchs_write_4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off + sc->mmchs_reg_off, val); } static inline uint32_t RD4(struct ti_sdhci_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off + sc->sdhci_reg_off)); } static inline void WR4(struct ti_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off + sc->sdhci_reg_off, val); } static uint8_t ti_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xff); } static uint16_t ti_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t clkdiv, val32; /* * The MMCHS hardware has a non-standard interpretation of the sdclock * divisor bits. It uses the same bit positions as SDHCI 3.0 (15..6) * but doesn't split them into low:high fields. Instead they're a * single number in the range 0..1023 and the number is exactly the * clock divisor (with 0 and 1 both meaning divide by 1). The SDHCI * driver code expects a v2.0 or v3.0 divisor. The shifting and masking * here extracts the MMCHS representation from the hardware word, cleans * those bits out, applies the 2N adjustment, and plugs the result into * the bit positions for the 2.0 or 3.0 divisor in the returned register * value. The ti_sdhci_write_2() routine performs the opposite * transformation when the SDHCI driver writes to the register. */ if (off == SDHCI_CLOCK_CONTROL) { val32 = RD4(sc, SDHCI_CLOCK_CONTROL); clkdiv = ((val32 >> MMCHS_SYSCTL_CLKD_SHIFT) & MMCHS_SYSCTL_CLKD_MASK) / 2; val32 &= ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT); val32 |= (clkdiv & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT; if (slot->version >= SDHCI_SPEC_300) val32 |= ((clkdiv >> SDHCI_DIVIDER_MASK_LEN) & SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_HI_SHIFT; return (val32 & 0xffff); } /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { return (sc->cmd_and_mode >> 16); } else if (off == SDHCI_COMMAND_FLAGS) { return (sc->cmd_and_mode & 0x0000ffff); } return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xffff); } static uint32_t ti_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; val32 = RD4(sc, off); /* * If we need to disallow highspeed mode due to the OMAP4 erratum, strip * that flag from the returned capabilities. */ if (off == SDHCI_CAPABILITIES && sc->disable_highspeed) val32 &= ~SDHCI_CAN_DO_HISPD; /* * Force the card-present state if necessary. */ if (off == SDHCI_PRESENT_STATE && sc->force_card_present) val32 |= SDHCI_CARD_PRESENT; return (val32); } static void ti_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct ti_sdhci_softc *sc = device_get_softc(dev); bus_read_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count); } static void ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3) * 8); val32 |= (val << (off & 3) * 8); WR4(sc, off & ~3, val32); } static void ti_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); uint32_t clkdiv, val32; /* * Translate between the hardware and SDHCI 2.0 or 3.0 representations * of the clock divisor. See the comments in ti_sdhci_read_2() for * details. */ if (off == SDHCI_CLOCK_CONTROL) { clkdiv = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK; if (slot->version >= SDHCI_SPEC_300) clkdiv |= ((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_MASK_LEN; clkdiv *= 2; if (clkdiv > MMCHS_SYSCTL_CLKD_MASK) clkdiv = MMCHS_SYSCTL_CLKD_MASK; val32 = RD4(sc, SDHCI_CLOCK_CONTROL); val32 &= 0xffff0000; val32 |= val & ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT); val32 |= clkdiv << MMCHS_SYSCTL_CLKD_SHIFT; WR4(sc, SDHCI_CLOCK_CONTROL, val32); return; } /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { sc->cmd_and_mode = (sc->cmd_and_mode & 0xffff0000) | ((uint32_t)val & 0x0000ffff); return; } else if (off == SDHCI_COMMAND_FLAGS) { sc->cmd_and_mode = (sc->cmd_and_mode & 0x0000ffff) | ((uint32_t)val << 16); WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode); return; } val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3) * 8); val32 |= ((val & 0xffff) << (off & 3) * 8); WR4(sc, off & ~3, val32); } static void ti_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct ti_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); } static void ti_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct ti_sdhci_softc *sc = device_get_softc(dev); bus_write_multi_4(sc->mem_res, off + sc->sdhci_reg_off, data, count); } static void ti_sdhci_intr(void *arg) { struct ti_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->slot); } static int ti_sdhci_update_ios(device_t brdev, device_t reqdev) { struct ti_sdhci_softc *sc = device_get_softc(brdev); struct sdhci_slot *slot; struct mmc_ios *ios; uint32_t val32; slot = device_get_ivars(reqdev); ios = &slot->host.ios; /* * There is an 8-bit-bus bit in the MMCHS control register which, when * set, overrides the 1 vs 4 bit setting in the standard SDHCI * registers. Set that bit first according to whether an 8-bit bus is * requested, then let the standard driver handle everything else. */ val32 = ti_mmchs_read_4(sc, MMCHS_CON); if (ios->bus_width == bus_width_8) ti_mmchs_write_4(sc, MMCHS_CON, val32 | MMCHS_CON_DW8); else ti_mmchs_write_4(sc, MMCHS_CON, val32 & ~MMCHS_CON_DW8); return (sdhci_generic_update_ios(brdev, reqdev)); } static int ti_sdhci_get_ro(device_t brdev, device_t reqdev) { struct ti_sdhci_softc *sc = device_get_softc(brdev); unsigned int readonly = 0; /* If a gpio pin is configured, read it. */ if (sc->gpio_dev != NULL) { GPIO_PIN_GET(sc->gpio_dev, sc->wp_gpio_pin, &readonly); } return (readonly); } static int ti_sdhci_detach(device_t dev) { return (EBUSY); } static void ti_sdhci_hw_init(device_t dev) { struct ti_sdhci_softc *sc = device_get_softc(dev); clk_ident_t clk; uint32_t regval; unsigned long timeout; /* Enable the controller and interface/functional clocks */ clk = MMC0_CLK + sc->mmchs_device_id; if (ti_prcm_clk_enable(clk) != 0) { device_printf(dev, "Error: failed to enable MMC clock\n"); return; } /* Get the frequency of the source clock */ if (ti_prcm_clk_get_source_freq(clk, &sc->baseclk_hz) != 0) { device_printf(dev, "Error: failed to get source clock freq\n"); return; } /* Issue a softreset to the controller */ ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET); timeout = 1000; while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSSTATUS_RESETDONE)) { if (--timeout == 0) { device_printf(dev, "Error: Controller reset operation timed out\n"); break; } DELAY(100); } /* Reset both the command and data state machines */ ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL); timeout = 1000; while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL)) { if (--timeout == 0) { device_printf(dev, "Error: Software reset operation timed out\n"); break; } DELAY(100); } /* * The attach() routine has examined fdt data and set flags in * slot.host.caps to reflect what voltages we can handle. Set those * values in the CAPA register. The manual says that these values can * only be set once, "before initialization" whatever that means, and * that they survive a reset. So maybe doing this will be a no-op if * u-boot has already initialized the hardware. */ regval = ti_mmchs_read_4(sc, MMCHS_SD_CAPA); if (sc->slot.host.caps & MMC_OCR_LOW_VOLTAGE) regval |= MMCHS_SD_CAPA_VS18; if (sc->slot.host.caps & (MMC_OCR_290_300 | MMC_OCR_300_310)) regval |= MMCHS_SD_CAPA_VS30; ti_mmchs_write_4(sc, MMCHS_SD_CAPA, regval); /* Set initial host configuration (1-bit, std speed, pwr off). */ ti_sdhci_write_1(dev, NULL, SDHCI_HOST_CONTROL, 0); ti_sdhci_write_1(dev, NULL, SDHCI_POWER_CONTROL, 0); /* Set the initial controller configuration. */ ti_mmchs_write_4(sc, MMCHS_CON, MMCHS_CON_DVAL_8_4MS); } static int ti_sdhci_attach(device_t dev) { struct ti_sdhci_softc *sc = device_get_softc(dev); int rid, err; pcell_t prop; phandle_t node; sc->dev = dev; /* * Get the MMCHS device id from FDT. If it's not there use the newbus * unit number (which will work as long as the devices are in order and * none are skipped in the fdt). Note that this is a property we made * up and added in freebsd, it doesn't exist in the published bindings. */ node = ofw_bus_get_node(dev); if ((OF_getprop(node, "mmchs-device-id", &prop, sizeof(prop))) <= 0) { sc->mmchs_device_id = device_get_unit(dev); device_printf(dev, "missing mmchs-device-id attribute in FDT, " "using unit number (%d)", sc->mmchs_device_id); } else sc->mmchs_device_id = fdt32_to_cpu(prop); /* * The hardware can inherently do dual-voltage (1p8v, 3p0v) on the first * device, and only 1p8v on other devices unless an external transceiver * is used. The only way we could know about a transceiver is fdt data. * Note that we have to do this before calling ti_sdhci_hw_init() so * that it can set the right values in the CAPA register, which can only * be done once and never reset. */ sc->slot.host.caps |= MMC_OCR_LOW_VOLTAGE; if (sc->mmchs_device_id == 0 || OF_hasprop(node, "ti,dual-volt")) { sc->slot.host.caps |= MMC_OCR_290_300 | MMC_OCR_300_310; } /* * See if we've got a GPIO-based write detect pin. This is not the * standard documented property for this, we added it in freebsd. */ if ((OF_getprop(node, "mmchs-wp-gpio-pin", &prop, sizeof(prop))) <= 0) sc->wp_gpio_pin = 0xffffffff; else sc->wp_gpio_pin = fdt32_to_cpu(prop); if (sc->wp_gpio_pin != 0xffffffff) { sc->gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc->gpio_dev == NULL) device_printf(dev, "Error: No GPIO device, " "Write Protect pin will not function\n"); else GPIO_PIN_SETFLAGS(sc->gpio_dev, sc->wp_gpio_pin, GPIO_PIN_INPUT); } /* * Set the offset from the device's memory start to the MMCHS registers. * Also for OMAP4 disable high speed mode due to erratum ID i626. */ - if (ti_chip() == CHIP_OMAP_3) - sc->mmchs_reg_off = OMAP3_MMCHS_REG_OFFSET; - else if (ti_chip() == CHIP_OMAP_4) { + if (ti_chip() == CHIP_OMAP_4) { sc->mmchs_reg_off = OMAP4_MMCHS_REG_OFFSET; sc->disable_highspeed = true; } else if (ti_chip() == CHIP_AM335X) sc->mmchs_reg_off = AM335X_MMCHS_REG_OFFSET; else panic("Unknown OMAP device\n"); /* * The standard SDHCI registers are at a fixed offset (the same on all * SoCs) beyond the MMCHS registers. */ sc->sdhci_reg_off = sc->mmchs_reg_off + SDHCI_REG_OFFSET; /* Resource setup. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, ti_sdhci_intr, sc, &sc->intr_cookie)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } /* Initialise the MMCHS hardware. */ ti_sdhci_hw_init(dev); /* * The capabilities register can only express base clock frequencies in * the range of 0-63MHz for a v2.0 controller. Since our clock runs * faster than that, the hardware sets the frequency to zero in the * register. When the register contains zero, the sdhci driver expects * slot.max_clk to already have the right value in it. */ sc->slot.max_clk = sc->baseclk_hz; /* * The MMCHS timeout counter is based on the output sdclock. Tell the * sdhci driver to recalculate the timeout clock whenever the output * sdclock frequency changes. */ sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; /* * The MMCHS hardware shifts the 136-bit response data (in violation of * the spec), so tell the sdhci driver not to do the same in software. */ sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE; /* * DMA is not really broken, I just haven't implemented it yet. */ sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA; /* * Set up the hardware and go. Note that this sets many of the * slot.host.* fields, so we have to do this before overriding any of * those values based on fdt data, below. */ sdhci_init_slot(dev, &sc->slot, 0); /* * The SDHCI controller doesn't realize it, but we can support 8-bit * even though we're not a v3.0 controller. If there's an fdt bus-width * property, honor it. */ if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) { sc->slot.host.caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA); switch (prop) { case 8: sc->slot.host.caps |= MMC_CAP_8_BIT_DATA; /* FALLTHROUGH */ case 4: sc->slot.host.caps |= MMC_CAP_4_BIT_DATA; break; case 1: break; default: device_printf(dev, "Bad bus-width value %u\n", prop); break; } } /* * If the slot is flagged with the non-removable property, set our flag * to always force the SDHCI_CARD_PRESENT bit on. */ node = ofw_bus_get_node(dev); if (OF_hasprop(node, "non-removable")) sc->force_card_present = true; bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->slot); return (0); fail: if (sc->intr_cookie) bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie); if (sc->irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (err); } static int ti_sdhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "TI MMCHS (SDHCI 2.0)"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static device_method_t ti_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_sdhci_probe), DEVMETHOD(device_attach, ti_sdhci_attach), DEVMETHOD(device_detach, ti_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, ti_sdhci_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, ti_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, ti_sdhci_read_1), DEVMETHOD(sdhci_read_2, ti_sdhci_read_2), DEVMETHOD(sdhci_read_4, ti_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, ti_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, ti_sdhci_write_1), DEVMETHOD(sdhci_write_2, ti_sdhci_write_2), DEVMETHOD(sdhci_write_4, ti_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, ti_sdhci_write_multi_4), DEVMETHOD_END }; static devclass_t ti_sdhci_devclass; static driver_t ti_sdhci_driver = { "sdhci_ti", ti_sdhci_methods, sizeof(struct ti_sdhci_softc), }; DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, 0, 0); MODULE_DEPEND(sdhci_ti, sdhci, 1, 1, 1);