diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /arch/arm/mach-omap1 | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/arm/mach-omap1')
78 files changed, 18365 insertions, 0 deletions
diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig new file mode 100644 index 000000000..7ec7ada28 --- /dev/null +++ b/arch/arm/mach-omap1/Kconfig @@ -0,0 +1,264 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig ARCH_OMAP1 + bool "TI OMAP1" + depends on ARCH_MULTI_V4T || ARCH_MULTI_V5 + depends on CPU_LITTLE_ENDIAN + depends on ATAGS + select ARCH_OMAP + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_OMAP + select CLKSRC_MMIO + select FORCE_PCI if PCCARD + select GPIOLIB + help + Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) + +if ARCH_OMAP1 + +menu "TI OMAP1 specific features" + +comment "OMAP Core Type" + +config ARCH_OMAP730 + depends on ARCH_MULTI_V5 + bool "OMAP730 Based System" + select ARCH_OMAP_OTG + select CPU_ARM926T + select OMAP_MPU_TIMER + +config ARCH_OMAP850 + depends on ARCH_MULTI_V5 + bool "OMAP850 Based System" + select ARCH_OMAP_OTG + select CPU_ARM926T + +config ARCH_OMAP15XX + depends on ARCH_MULTI_V4T + default y + bool "OMAP15xx Based System" + select CPU_ARM925T + select OMAP_MPU_TIMER + +config ARCH_OMAP16XX + depends on ARCH_MULTI_V5 + bool "OMAP16xx Based System" + select ARCH_OMAP_OTG + select CPU_ARM926T + select OMAP_DM_TIMER + +config ARCH_OMAP + bool + +comment "OMAP Feature Selections" + +config OMAP_MUX + bool "OMAP multiplexing support" + default y + help + Pin multiplexing support for OMAP boards. If your bootloader + sets the multiplexing correctly, say N. Otherwise, or if unsure, + say Y. + +config OMAP_MUX_DEBUG + bool "Multiplexing debug output" + depends on OMAP_MUX + help + Makes the multiplexing functions print out a lot of debug info. + This is useful if you want to find out the correct values of the + multiplexing registers. + +config OMAP_MUX_WARNINGS + bool "Warn about pins the bootloader didn't set up" + depends on OMAP_MUX + default y + help + Choose Y here to warn whenever driver initialization logic needs + to change the pin multiplexing setup. When there are no warnings + printed, it's safe to deselect OMAP_MUX for your product. + +config OMAP_32K_TIMER + bool "Use 32KHz timer" + depends on ARCH_OMAP16XX + default ARCH_OMAP16XX + help + Select this option if you want to enable the OMAP 32KHz timer. + This timer saves power compared to the OMAP_MPU_TIMER, and has + support for no tick during idle. The 32KHz timer provides less + intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is + currently only available for OMAP16XX, 24XX, 34XX, OMAP4/5 and DRA7XX. + + On OMAP2PLUS this value is only used for CONFIG_HZ and + CLOCK_TICK_RATE compile time calculation. + The actual timer selection is done in the board file + through the (DT_)MACHINE_START structure. + +config OMAP_MPU_TIMER + bool "Use mpu timer" + depends on ARCH_OMAP1 + help + Select this option if you want to use the OMAP mpu timer. This + timer provides more intra-tick resolution than the 32KHz timer, + but consumes more power. + +config OMAP_SERIAL_WAKE + bool "Enable wake-up events for serial ports" + depends on ARCH_OMAP1 && OMAP_MUX + default y + help + Select this option if you want to have your system wake up + to data on the serial RX line. This allows you to wake the + system from serial console. + +config OMAP_RESET_CLOCKS + bool "Reset unused clocks during boot" + depends on ARCH_OMAP + help + Say Y if you want to reset unused clocks during boot. + This option saves power, but assumes all drivers are + using the clock framework. Broken drivers that do not + yet use clock framework may not work with this option. + If you are booting from another operating system, you + probably do not want this option enabled until your + device drivers work properly. + +config ARCH_OMAP_OTG + bool + +comment "OMAP Board Type" + +config MACH_OMAP_INNOVATOR + bool "TI Innovator" + depends on ARCH_OMAP15XX || ARCH_OMAP16XX + depends on UNUSED_BOARD_FILES + help + TI OMAP 1510 or 1610 Innovator board support. Say Y here if you + have such a board. + +config MACH_OMAP_H2 + bool "TI H2 Support" + depends on ARCH_OMAP16XX + depends on UNUSED_BOARD_FILES + help + TI OMAP 1610/1611B H2 board support. Say Y here if you have such + a board. + +config MACH_OMAP_H3 + bool "TI H3 Support" + depends on ARCH_OMAP16XX + depends on UNUSED_BOARD_FILES + help + TI OMAP 1710 H3 board support. Say Y here if you have such + a board. + +config MACH_HERALD + bool "HTC Herald" + depends on ARCH_OMAP850 + depends on UNUSED_BOARD_FILES + help + HTC Herald smartphone support (AKA T-Mobile Wing, ...) + +config MACH_OMAP_OSK + bool "TI OSK Support" + depends on ARCH_OMAP16XX + help + TI OMAP 5912 OSK (OMAP Starter Kit) board support. Say Y here + if you have such a board. + +config OMAP_OSK_MISTRAL + bool "Mistral QVGA board Support" + depends on MACH_OMAP_OSK + depends on UNUSED_BOARD_FILES + help + The OSK supports an optional add-on board with a Quarter-VGA + touchscreen, PDA-ish buttons, a resume button, bicolor LED, + and camera connector. Say Y here if you have this board. + +config MACH_OMAP_PERSEUS2 + bool "TI Perseus2" + depends on ARCH_OMAP730 + depends on UNUSED_BOARD_FILES + help + Support for TI OMAP 730 Perseus2 board. Say Y here if you have such + a board. + +config MACH_OMAP_FSAMPLE + bool "TI F-Sample" + depends on ARCH_OMAP730 + depends on UNUSED_BOARD_FILES + help + Support for TI OMAP 850 F-Sample board. Say Y here if you have such + a board. + +config MACH_OMAP_PALMTE + bool "Palm Tungsten E" + depends on ARCH_OMAP15XX + help + Support for the Palm Tungsten E PDA. To boot the kernel, you'll + need a PalmOS compatible bootloader; check out + http://palmtelinux.sourceforge.net/ for more information. + Say Y here if you have this PDA model, say N otherwise. + +config MACH_OMAP_PALMZ71 + bool "Palm Zire71" + depends on ARCH_OMAP15XX + depends on UNUSED_BOARD_FILES + help + Support for the Palm Zire71 PDA. To boot the kernel, + you'll need a PalmOS compatible bootloader; check out + http://hackndev.com/palm/z71 for more information. + Say Y here if you have such a PDA, say N otherwise. + +config MACH_OMAP_PALMTT + bool "Palm Tungsten|T" + depends on ARCH_OMAP15XX + depends on UNUSED_BOARD_FILES + help + Support for the Palm Tungsten|T PDA. To boot the kernel, you'll + need a PalmOS compatible bootloader (Garux); check out + http://garux.sourceforge.net/ for more information. + Say Y here if you have this PDA model, say N otherwise. + +config MACH_SX1 + bool "Siemens SX1" + depends on ARCH_OMAP15XX + select I2C + help + Support for the Siemens SX1 phone. To boot the kernel, + you'll need a SX1 compatible bootloader; check out + http://forum.oslik.ru and + https://www.handhelds.org/moin/moin.cgi/SiemensSX1 + for more information. + Say Y here if you have such a phone, say NO otherwise. + +config MACH_NOKIA770 + bool "Nokia 770" + depends on ARCH_OMAP16XX + help + Support for the Nokia 770 Internet Tablet. Say Y here if you + have such a device. + +config MACH_AMS_DELTA + bool "Amstrad E3 (Delta)" + depends on ARCH_OMAP15XX + select FIQ + select GPIO_GENERIC_PLATFORM + select LEDS_GPIO_REGISTER + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + Support for the Amstrad E3 (codename Delta) videophone. Say Y here + if you have such a device. + +config MACH_OMAP_GENERIC + bool "Generic OMAP board" + depends on ARCH_OMAP15XX || ARCH_OMAP16XX + depends on UNUSED_BOARD_FILES + help + Support for generic OMAP-1510, 1610 or 1710 board with + no FPGA. Can be used as template for porting Linux to + custom OMAP boards. Say Y here if you have a custom + board. + +endmenu + +endif diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile new file mode 100644 index 000000000..0615cb0ba --- /dev/null +++ b/arch/arm/mach-omap1/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := io.o id.o sram-init.o sram.o time.o irq.o mux.o flash.o \ + serial.o devices.o dma.o omap-dma.o fb.o +obj-y += clock.o clock_data.o opp_data.o reset.o pm_bus.o timer.o + +ifneq ($(CONFIG_SND_SOC_OMAP_MCBSP),) +obj-y += mcbsp.o +endif + +obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o + +# OCPI interconnect support for 1710, 1610 and 5912 +obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o + +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + +i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o +obj-y += $(i2c-omap-m) $(i2c-omap-y) + +led-y := leds.o + +usb-fs-$(CONFIG_USB_SUPPORT) := usb.o +obj-y += $(usb-fs-m) $(usb-fs-y) + +# Specific board support +obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o board-h2-mmc.o \ + board-nand.o +obj-$(CONFIG_MACH_OMAP_INNOVATOR) += board-innovator.o +obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o +obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o board-nand.o +obj-$(CONFIG_MACH_OMAP_FSAMPLE) += board-fsample.o board-nand.o +obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o +obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o board-h3-mmc.o \ + board-nand.o +obj-$(CONFIG_MACH_OMAP_PALMTE) += board-palmte.o +obj-$(CONFIG_MACH_OMAP_PALMZ71) += board-palmz71.o +obj-$(CONFIG_MACH_OMAP_PALMTT) += board-palmtt.o +obj-$(CONFIG_MACH_NOKIA770) += board-nokia770.o +obj-$(CONFIG_MACH_AMS_DELTA) += board-ams-delta.o ams-delta-fiq.o \ + ams-delta-fiq-handler.o +obj-$(CONFIG_MACH_SX1) += board-sx1.o board-sx1-mmc.o +obj-$(CONFIG_MACH_HERALD) += board-htcherald.o + +ifeq ($(CONFIG_ARCH_OMAP15XX),y) +# Innovator-1510 FPGA +obj-$(CONFIG_MACH_OMAP_INNOVATOR) += fpga.o +endif + +# GPIO +obj-$(CONFIG_ARCH_OMAP730) += gpio7xx.o +obj-$(CONFIG_ARCH_OMAP850) += gpio7xx.o +obj-$(CONFIG_ARCH_OMAP15XX) += gpio15xx.o +obj-$(CONFIG_ARCH_OMAP16XX) += gpio16xx.o diff --git a/arch/arm/mach-omap1/ams-delta-fiq-handler.S b/arch/arm/mach-omap1/ams-delta-fiq-handler.S new file mode 100644 index 000000000..35c2f9574 --- /dev/null +++ b/arch/arm/mach-omap1/ams-delta-fiq-handler.S @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/mach-omap1/ams-delta-fiq-handler.S + * + * Based on linux/arch/arm/lib/floppydma.S + * Renamed and modified to work with 2.6 kernel by Matt Callow + * Copyright (C) 1995, 1996 Russell King + * Copyright (C) 2004 Pete Trapps + * Copyright (C) 2006 Matt Callow + * Copyright (C) 2010 Janusz Krzysztofik + */ + +#include <linux/linkage.h> +#include <linux/platform_data/ams-delta-fiq.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/assembler.h> +#include <asm/irq.h> + +#include "hardware.h" +#include "ams-delta-fiq.h" +#include "board-ams-delta.h" +#include "iomap.h" + +/* + * OMAP1510 GPIO related symbol copied from arch/arm/mach-omap1/gpio15xx.c. + * Unfortunately, it was not placed in a separate header file. + */ +#define OMAP1510_GPIO_BASE 0xFFFCE000 + +/* GPIO register bitmasks */ +#define KEYBRD_DATA_MASK (0x1 << AMS_DELTA_GPIO_PIN_KEYBRD_DATA) +#define KEYBRD_CLK_MASK (0x1 << AMS_DELTA_GPIO_PIN_KEYBRD_CLK) +#define MODEM_IRQ_MASK (0x1 << AMS_DELTA_GPIO_PIN_MODEM_IRQ) +#define HOOK_SWITCH_MASK (0x1 << AMS_DELTA_GPIO_PIN_HOOK_SWITCH) +#define OTHERS_MASK (MODEM_IRQ_MASK | HOOK_SWITCH_MASK) + +/* IRQ handler register bitmasks */ +#define DEFERRED_FIQ_MASK OMAP_IRQ_BIT(INT_DEFERRED_FIQ) +#define GPIO_BANK1_MASK OMAP_IRQ_BIT(INT_GPIO_BANK1) + +/* Driver buffer byte offsets */ +#define BUF_MASK (FIQ_MASK * 4) +#define BUF_STATE (FIQ_STATE * 4) +#define BUF_KEYS_CNT (FIQ_KEYS_CNT * 4) +#define BUF_TAIL_OFFSET (FIQ_TAIL_OFFSET * 4) +#define BUF_HEAD_OFFSET (FIQ_HEAD_OFFSET * 4) +#define BUF_BUF_LEN (FIQ_BUF_LEN * 4) +#define BUF_KEY (FIQ_KEY * 4) +#define BUF_MISSED_KEYS (FIQ_MISSED_KEYS * 4) +#define BUF_BUFFER_START (FIQ_BUFFER_START * 4) +#define BUF_GPIO_INT_MASK (FIQ_GPIO_INT_MASK * 4) +#define BUF_KEYS_HICNT (FIQ_KEYS_HICNT * 4) +#define BUF_IRQ_PEND (FIQ_IRQ_PEND * 4) +#define BUF_SIR_CODE_L1 (FIQ_SIR_CODE_L1 * 4) +#define BUF_SIR_CODE_L2 (IRQ_SIR_CODE_L2 * 4) +#define BUF_CNT_INT_00 (FIQ_CNT_INT_00 * 4) +#define BUF_CNT_INT_KEY (FIQ_CNT_INT_KEY * 4) +#define BUF_CNT_INT_MDM (FIQ_CNT_INT_MDM * 4) +#define BUF_CNT_INT_03 (FIQ_CNT_INT_03 * 4) +#define BUF_CNT_INT_HSW (FIQ_CNT_INT_HSW * 4) +#define BUF_CNT_INT_05 (FIQ_CNT_INT_05 * 4) +#define BUF_CNT_INT_06 (FIQ_CNT_INT_06 * 4) +#define BUF_CNT_INT_07 (FIQ_CNT_INT_07 * 4) +#define BUF_CNT_INT_08 (FIQ_CNT_INT_08 * 4) +#define BUF_CNT_INT_09 (FIQ_CNT_INT_09 * 4) +#define BUF_CNT_INT_10 (FIQ_CNT_INT_10 * 4) +#define BUF_CNT_INT_11 (FIQ_CNT_INT_11 * 4) +#define BUF_CNT_INT_12 (FIQ_CNT_INT_12 * 4) +#define BUF_CNT_INT_13 (FIQ_CNT_INT_13 * 4) +#define BUF_CNT_INT_14 (FIQ_CNT_INT_14 * 4) +#define BUF_CNT_INT_15 (FIQ_CNT_INT_15 * 4) +#define BUF_CIRC_BUFF (FIQ_CIRC_BUFF * 4) + + +/* + * Register usage + * r8 - temporary + * r9 - the driver buffer + * r10 - temporary + * r11 - interrupts mask + * r12 - base pointers + * r13 - interrupts status + */ + + .text + + .global qwerty_fiqin_end + +ENTRY(qwerty_fiqin_start) + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @ FIQ intrrupt handler + ldr r12, omap_ih1_base @ set pointer to level1 handler + + ldr r11, [r12, #IRQ_MIR_REG_OFFSET] @ fetch interrupts mask + + ldr r13, [r12, #IRQ_ITR_REG_OFFSET] @ fetch interrupts status + bics r13, r13, r11 @ clear masked - any left? + beq exit @ none - spurious FIQ? exit + + ldr r10, [r12, #IRQ_SIR_FIQ_REG_OFFSET] @ get requested interrupt number + + mov r8, #2 @ reset FIQ agreement + str r8, [r12, #IRQ_CONTROL_REG_OFFSET] + + cmp r10, #(INT_GPIO_BANK1 - NR_IRQS_LEGACY) @ is it GPIO interrupt? + beq gpio @ yes - process it + + mov r8, #1 + orr r8, r11, r8, lsl r10 @ mask spurious interrupt + str r8, [r12, #IRQ_MIR_REG_OFFSET] +exit: + subs pc, lr, #4 @ return from FIQ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + + @@@@@@@@@@@@@@@@@@@@@@@@@@@ +gpio: @ GPIO bank interrupt handler + ldr r12, omap1510_gpio_base @ set base pointer to GPIO bank + + ldr r11, [r12, #OMAP1510_GPIO_INT_MASK] @ fetch GPIO interrupts mask +restart: + ldr r13, [r12, #OMAP1510_GPIO_INT_STATUS] @ fetch status bits + bics r13, r13, r11 @ clear masked - any left? + beq exit @ no - spurious interrupt? exit + + orr r11, r11, r13 @ mask all requested interrupts + str r11, [r12, #OMAP1510_GPIO_INT_MASK] + + str r13, [r12, #OMAP1510_GPIO_INT_STATUS] @ ack all requested interrupts + + ands r10, r13, #KEYBRD_CLK_MASK @ extract keyboard status - set? + beq hksw @ no - try next source + + + @@@@@@@@@@@@@@@@@@@@@@ + @ Keyboard clock FIQ mode interrupt handler + @ r10 now contains KEYBRD_CLK_MASK, use it + bic r11, r11, r10 @ unmask it + str r11, [r12, #OMAP1510_GPIO_INT_MASK] + + @ Process keyboard data + ldr r8, [r12, #OMAP1510_GPIO_DATA_INPUT] @ fetch GPIO input + + ldr r10, [r9, #BUF_STATE] @ fetch kbd interface state + cmp r10, #0 @ are we expecting start bit? + bne data @ no - go to data processing + + ands r8, r8, #KEYBRD_DATA_MASK @ check start bit - detected? + beq hksw @ no - try next source + + @ r8 contains KEYBRD_DATA_MASK, use it + str r8, [r9, #BUF_STATE] @ enter data processing state + @ r10 already contains 0, reuse it + str r10, [r9, #BUF_KEY] @ clear keycode + mov r10, #2 @ reset input bit mask + str r10, [r9, #BUF_MASK] + + @ Mask other GPIO line interrupts till key done + str r11, [r9, #BUF_GPIO_INT_MASK] @ save mask for later restore + mvn r11, #KEYBRD_CLK_MASK @ prepare all except kbd mask + str r11, [r12, #OMAP1510_GPIO_INT_MASK] @ store into the mask register + + b restart @ restart + +data: ldr r10, [r9, #BUF_MASK] @ fetch current input bit mask + + @ r8 still contains GPIO input bits + ands r8, r8, #KEYBRD_DATA_MASK @ is keyboard data line low? + ldreq r8, [r9, #BUF_KEY] @ yes - fetch collected so far, + orreq r8, r8, r10 @ set 1 at current mask position + streq r8, [r9, #BUF_KEY] @ and save back + + mov r10, r10, lsl #1 @ shift mask left + bics r10, r10, #0x800 @ have we got all the bits? + strne r10, [r9, #BUF_MASK] @ not yet - store the mask + bne restart @ and restart + + @ r10 already contains 0, reuse it + str r10, [r9, #BUF_STATE] @ reset state to start + + @ Key done - restore interrupt mask + ldr r10, [r9, #BUF_GPIO_INT_MASK] @ fetch saved mask + and r11, r11, r10 @ unmask all saved as unmasked + str r11, [r12, #OMAP1510_GPIO_INT_MASK] @ restore into the mask register + + @ Try appending the keycode to the circular buffer + ldr r10, [r9, #BUF_KEYS_CNT] @ get saved keystrokes count + ldr r8, [r9, #BUF_BUF_LEN] @ get buffer size + cmp r10, r8 @ is buffer full? + beq hksw @ yes - key lost, next source + + add r10, r10, #1 @ incremet keystrokes counter + str r10, [r9, #BUF_KEYS_CNT] + + ldr r10, [r9, #BUF_TAIL_OFFSET] @ get buffer tail offset + @ r8 already contains buffer size + cmp r10, r8 @ end of buffer? + moveq r10, #0 @ yes - rewind to buffer start + + ldr r12, [r9, #BUF_BUFFER_START] @ get buffer start address + add r12, r12, r10, LSL #2 @ calculate buffer tail address + ldr r8, [r9, #BUF_KEY] @ get last keycode + str r8, [r12] @ append it to the buffer tail + + add r10, r10, #1 @ increment buffer tail offset + str r10, [r9, #BUF_TAIL_OFFSET] + + ldr r10, [r9, #BUF_CNT_INT_KEY] @ increment interrupts counter + add r10, r10, #1 + str r10, [r9, #BUF_CNT_INT_KEY] + @@@@@@@@@@@@@@@@@@@@@@@@ + + +hksw: @Is hook switch interrupt requested? + tst r13, #HOOK_SWITCH_MASK @ is hook switch status bit set? + beq mdm @ no - try next source + + + @@@@@@@@@@@@@@@@@@@@@@@@ + @ Hook switch interrupt FIQ mode simple handler + + @ Don't toggle active edge, the switch always bounces + + @ Increment hook switch interrupt counter + ldr r10, [r9, #BUF_CNT_INT_HSW] + add r10, r10, #1 + str r10, [r9, #BUF_CNT_INT_HSW] + @@@@@@@@@@@@@@@@@@@@@@@@ + + +mdm: @Is it a modem interrupt? + tst r13, #MODEM_IRQ_MASK @ is modem status bit set? + beq irq @ no - check for next interrupt + + + @@@@@@@@@@@@@@@@@@@@@@@@ + @ Modem FIQ mode interrupt handler stub + + @ Increment modem interrupt counter + ldr r10, [r9, #BUF_CNT_INT_MDM] + add r10, r10, #1 + str r10, [r9, #BUF_CNT_INT_MDM] + @@@@@@@@@@@@@@@@@@@@@@@@ + + +irq: @ Place deferred_fiq interrupt request + ldr r12, deferred_fiq_ih_base @ set pointer to IRQ handler + mov r10, #DEFERRED_FIQ_MASK @ set deferred_fiq bit + str r10, [r12, #IRQ_ISR_REG_OFFSET] @ place it in the ISR register + + ldr r12, omap1510_gpio_base @ set pointer back to GPIO bank + b restart @ check for next GPIO interrupt + @@@@@@@@@@@@@@@@@@@@@@@@@@@ + + +/* + * Virtual addresses for IO + */ +omap_ih1_base: + .word OMAP1_IO_ADDRESS(OMAP_IH1_BASE) +deferred_fiq_ih_base: + .word OMAP1_IO_ADDRESS(DEFERRED_FIQ_IH_BASE) +omap1510_gpio_base: + .word OMAP1_IO_ADDRESS(OMAP1510_GPIO_BASE) +qwerty_fiqin_end: + +/* + * Check the size of the FIQ, + * it cannot go beyond 0xffff0200, and is copied to 0xffff001c + */ +.if (qwerty_fiqin_end - qwerty_fiqin_start) > (0x200 - 0x1c) + .err +.endif diff --git a/arch/arm/mach-omap1/ams-delta-fiq.c b/arch/arm/mach-omap1/ams-delta-fiq.c new file mode 100644 index 000000000..1f5852be0 --- /dev/null +++ b/arch/arm/mach-omap1/ams-delta-fiq.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Amstrad E3 FIQ handling + * + * Copyright (C) 2009 Janusz Krzysztofik + * Copyright (c) 2006 Matt Callow + * Copyright (c) 2004 Amstrad Plc + * Copyright (C) 2001 RidgeRun, Inc. + * + * Parts of this code are taken from linux/arch/arm/mach-omap/irq.c + * in the MontaVista 2.4 kernel (and the Amstrad changes therein) + */ +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_data/ams-delta-fiq.h> +#include <linux/platform_device.h> + +#include <asm/fiq.h> +#include <linux/soc/ti/omap1-io.h> + +#include "hardware.h" +#include "ams-delta-fiq.h" +#include "board-ams-delta.h" + +static struct fiq_handler fh = { + .name = "ams-delta-fiq" +}; + +/* + * This buffer is shared between FIQ and IRQ contexts. + * The FIQ and IRQ isrs can both read and write it. + * It is structured as a header section several 32bit slots, + * followed by the circular buffer where the FIQ isr stores + * keystrokes received from the qwerty keyboard. See + * <linux/platform_data/ams-delta-fiq.h> for details of offsets. + */ +static unsigned int fiq_buffer[1024]; + +static struct irq_chip *irq_chip; +static struct irq_data *irq_data[16]; +static unsigned int irq_counter[16]; + +static const char *pin_name[16] __initconst = { + [AMS_DELTA_GPIO_PIN_KEYBRD_DATA] = "keybrd_data", + [AMS_DELTA_GPIO_PIN_KEYBRD_CLK] = "keybrd_clk", +}; + +static irqreturn_t deferred_fiq(int irq, void *dev_id) +{ + struct irq_data *d; + int gpio, irq_num, fiq_count; + + /* + * For each handled GPIO interrupt, keep calling its interrupt handler + * until the IRQ counter catches the FIQ incremented interrupt counter. + */ + for (gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK; + gpio <= AMS_DELTA_GPIO_PIN_HOOK_SWITCH; gpio++) { + d = irq_data[gpio]; + irq_num = d->irq; + fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio]; + + if (irq_counter[gpio] < fiq_count && + gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) { + /* + * handle_simple_irq() that OMAP GPIO edge + * interrupts default to since commit 80ac93c27441 + * requires interrupt already acked and unmasked. + */ + if (!WARN_ON_ONCE(!irq_chip->irq_unmask)) + irq_chip->irq_unmask(d); + } + for (; irq_counter[gpio] < fiq_count; irq_counter[gpio]++) + generic_handle_irq(irq_num); + } + return IRQ_HANDLED; +} + +void __init ams_delta_init_fiq(struct gpio_chip *chip, + struct platform_device *serio) +{ + struct gpio_desc *gpiod, *data = NULL, *clk = NULL; + void *fiqhandler_start; + unsigned int fiqhandler_length; + struct pt_regs FIQ_regs; + unsigned long val, offset; + int i, retval; + + /* Store irq_chip location for IRQ handler use */ + irq_chip = chip->irq.chip; + if (!irq_chip) { + pr_err("%s: GPIO chip %s is missing IRQ function\n", __func__, + chip->label); + return; + } + + for (i = 0; i < ARRAY_SIZE(irq_data); i++) { + gpiod = gpiochip_request_own_desc(chip, i, pin_name[i], + GPIO_ACTIVE_HIGH, GPIOD_IN); + if (IS_ERR(gpiod)) { + pr_err("%s: failed to get GPIO pin %d (%ld)\n", + __func__, i, PTR_ERR(gpiod)); + return; + } + /* Store irq_data location for IRQ handler use */ + irq_data[i] = irq_get_irq_data(gpiod_to_irq(gpiod)); + + /* + * FIQ handler takes full control over serio data and clk GPIO + * pins. Initialize them and keep requested so nobody can + * interfere. Fail if any of those two couldn't be requested. + */ + switch (i) { + case AMS_DELTA_GPIO_PIN_KEYBRD_DATA: + data = gpiod; + gpiod_direction_input(data); + break; + case AMS_DELTA_GPIO_PIN_KEYBRD_CLK: + clk = gpiod; + gpiod_direction_input(clk); + break; + default: + gpiochip_free_own_desc(gpiod); + break; + } + } + if (!data || !clk) + goto out_gpio; + + fiqhandler_start = &qwerty_fiqin_start; + fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start; + pr_info("Installing fiq handler from %p, length 0x%x\n", + fiqhandler_start, fiqhandler_length); + + retval = claim_fiq(&fh); + if (retval) { + pr_err("ams_delta_init_fiq(): couldn't claim FIQ, ret=%d\n", + retval); + goto out_gpio; + } + + retval = request_irq(INT_DEFERRED_FIQ, deferred_fiq, + IRQ_TYPE_EDGE_RISING, "deferred_fiq", NULL); + if (retval < 0) { + pr_err("Failed to get deferred_fiq IRQ, ret=%d\n", retval); + release_fiq(&fh); + goto out_gpio; + } + /* + * Since no set_type() method is provided by OMAP irq chip, + * switch to edge triggered interrupt type manually. + */ + offset = IRQ_ILR0_REG_OFFSET + + ((INT_DEFERRED_FIQ - NR_IRQS_LEGACY) & 0x1f) * 0x4; + val = omap_readl(DEFERRED_FIQ_IH_BASE + offset) & ~(1 << 1); + omap_writel(val, DEFERRED_FIQ_IH_BASE + offset); + + set_fiq_handler(fiqhandler_start, fiqhandler_length); + + /* + * Initialise the buffer which is shared + * between FIQ mode and IRQ mode + */ + fiq_buffer[FIQ_GPIO_INT_MASK] = 0; + fiq_buffer[FIQ_MASK] = 0; + fiq_buffer[FIQ_STATE] = 0; + fiq_buffer[FIQ_KEY] = 0; + fiq_buffer[FIQ_KEYS_CNT] = 0; + fiq_buffer[FIQ_KEYS_HICNT] = 0; + fiq_buffer[FIQ_TAIL_OFFSET] = 0; + fiq_buffer[FIQ_HEAD_OFFSET] = 0; + fiq_buffer[FIQ_BUF_LEN] = 256; + fiq_buffer[FIQ_MISSED_KEYS] = 0; + fiq_buffer[FIQ_BUFFER_START] = + (unsigned int) &fiq_buffer[FIQ_CIRC_BUFF]; + + for (i = FIQ_CNT_INT_00; i <= FIQ_CNT_INT_15; i++) + fiq_buffer[i] = 0; + + /* + * FIQ mode r9 always points to the fiq_buffer, because the FIQ isr + * will run in an unpredictable context. The fiq_buffer is the FIQ isr's + * only means of communication with the IRQ level and other kernel + * context code. + */ + FIQ_regs.ARM_r9 = (unsigned int)fiq_buffer; + set_fiq_regs(&FIQ_regs); + + pr_info("request_fiq(): fiq_buffer = %p\n", fiq_buffer); + + /* + * Redirect GPIO interrupts to FIQ + */ + offset = IRQ_ILR0_REG_OFFSET + (INT_GPIO_BANK1 - NR_IRQS_LEGACY) * 0x4; + val = omap_readl(OMAP_IH1_BASE + offset) | 1; + omap_writel(val, OMAP_IH1_BASE + offset); + + /* Initialize serio device IRQ resource and platform_data */ + serio->resource[0].start = gpiod_to_irq(clk); + serio->resource[0].end = serio->resource[0].start; + serio->dev.platform_data = fiq_buffer; + + /* + * Since FIQ handler performs handling of GPIO registers for + * "keybrd_clk" IRQ pin, ams_delta_serio driver used to set + * handle_simple_irq() as active IRQ handler for that pin to avoid + * bad interaction with gpio-omap driver. This is no longer needed + * as handle_simple_irq() is now the default handler for OMAP GPIO + * edge interrupts. + * This comment replaces the obsolete code which has been removed + * from the ams_delta_serio driver and stands here only as a reminder + * of that dependency on gpio-omap driver behavior. + */ + + return; + +out_gpio: + if (data) + gpiochip_free_own_desc(data); + if (clk) + gpiochip_free_own_desc(clk); +} diff --git a/arch/arm/mach-omap1/ams-delta-fiq.h b/arch/arm/mach-omap1/ams-delta-fiq.h new file mode 100644 index 000000000..7f843caed --- /dev/null +++ b/arch/arm/mach-omap1/ams-delta-fiq.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * arch/arm/mach-omap1/ams-delta-fiq.h + * + * Taken from the original Amstrad modifications to fiq.h + * + * Copyright (c) 2004 Amstrad Plc + * Copyright (c) 2006 Matt Callow + * Copyright (c) 2010 Janusz Krzysztofik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __AMS_DELTA_FIQ_H +#define __AMS_DELTA_FIQ_H + +#include "irqs.h" + +/* + * Interrupt number used for passing control from FIQ to IRQ. + * IRQ12, described as reserved, has been selected. + */ +#define INT_DEFERRED_FIQ INT_1510_RES12 +/* + * Base address of an interrupt handler that the INT_DEFERRED_FIQ belongs to. + */ +#if (INT_DEFERRED_FIQ < IH2_BASE) +#define DEFERRED_FIQ_IH_BASE OMAP_IH1_BASE +#else +#define DEFERRED_FIQ_IH_BASE OMAP_IH2_BASE +#endif + +#ifndef __ASSEMBLER__ +extern unsigned char qwerty_fiqin_start, qwerty_fiqin_end; + +extern void __init ams_delta_init_fiq(struct gpio_chip *chip, + struct platform_device *pdev); +#endif + +#endif diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c new file mode 100644 index 000000000..651c28d81 --- /dev/null +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-ams-delta.c + * + * Modified from board-generic.c + * + * Board specific inits for the Amstrad E3 (codename Delta) videophone + * + * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> + */ +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/leds.h> +#include <linux/mtd/nand-gpio.h> +#include <linux/mtd/partitions.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/fixed.h> +#include <linux/regulator/machine.h> +#include <linux/serial_8250.h> +#include <linux/export.h> +#include <linux/omapfb.h> +#include <linux/io.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/soc/ti/omap1-mux.h> + +#include <asm/serial.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <linux/platform_data/keypad-omap.h> + +#include "hardware.h" +#include "usb.h" +#include "ams-delta-fiq.h" +#include "board-ams-delta.h" +#include "iomap.h" +#include "common.h" + +static const unsigned int ams_delta_keymap[] = { + KEY(0, 0, KEY_F1), /* Advert */ + + KEY(0, 3, KEY_COFFEE), /* Games */ + KEY(0, 2, KEY_QUESTION), /* Directory */ + KEY(2, 3, KEY_CONNECT), /* Internet */ + KEY(1, 2, KEY_SHOP), /* Services */ + KEY(1, 1, KEY_PHONE), /* VoiceMail */ + + KEY(0, 1, KEY_DELETE), /* Delete */ + KEY(2, 2, KEY_PLAY), /* Play */ + KEY(1, 0, KEY_PAGEUP), /* Up */ + KEY(1, 3, KEY_PAGEDOWN), /* Down */ + KEY(2, 0, KEY_EMAIL), /* ReadEmail */ + KEY(2, 1, KEY_STOP), /* Stop */ + + /* Numeric keypad portion */ + KEY(0, 7, KEY_KP1), + KEY(0, 6, KEY_KP2), + KEY(0, 5, KEY_KP3), + KEY(1, 7, KEY_KP4), + KEY(1, 6, KEY_KP5), + KEY(1, 5, KEY_KP6), + KEY(2, 7, KEY_KP7), + KEY(2, 6, KEY_KP8), + KEY(2, 5, KEY_KP9), + KEY(3, 6, KEY_KP0), + KEY(3, 7, KEY_KPASTERISK), + KEY(3, 5, KEY_KPDOT), /* # key */ + KEY(7, 2, KEY_NUMLOCK), /* Mute */ + KEY(7, 1, KEY_KPMINUS), /* Recall */ + KEY(6, 1, KEY_KPPLUS), /* Redial */ + KEY(7, 6, KEY_KPSLASH), /* Handsfree */ + KEY(6, 0, KEY_ENTER), /* Video */ + + KEY(7, 4, KEY_CAMERA), /* Photo */ + + KEY(0, 4, KEY_F2), /* Home */ + KEY(1, 4, KEY_F3), /* Office */ + KEY(2, 4, KEY_F4), /* Mobile */ + KEY(7, 7, KEY_F5), /* SMS */ + KEY(7, 5, KEY_F6), /* Email */ + + /* QWERTY portion of keypad */ + KEY(3, 4, KEY_Q), + KEY(3, 3, KEY_W), + KEY(3, 2, KEY_E), + KEY(3, 1, KEY_R), + KEY(3, 0, KEY_T), + KEY(4, 7, KEY_Y), + KEY(4, 6, KEY_U), + KEY(4, 5, KEY_I), + KEY(4, 4, KEY_O), + KEY(4, 3, KEY_P), + + KEY(4, 2, KEY_A), + KEY(4, 1, KEY_S), + KEY(4, 0, KEY_D), + KEY(5, 7, KEY_F), + KEY(5, 6, KEY_G), + KEY(5, 5, KEY_H), + KEY(5, 4, KEY_J), + KEY(5, 3, KEY_K), + KEY(5, 2, KEY_L), + + KEY(5, 1, KEY_Z), + KEY(5, 0, KEY_X), + KEY(6, 7, KEY_C), + KEY(6, 6, KEY_V), + KEY(6, 5, KEY_B), + KEY(6, 4, KEY_N), + KEY(6, 3, KEY_M), + KEY(6, 2, KEY_SPACE), + + KEY(7, 0, KEY_LEFTSHIFT), /* Vol up */ + KEY(7, 3, KEY_LEFTCTRL), /* Vol down */ +}; + +#define LATCH1_PHYS 0x01000000 +#define LATCH1_VIRT 0xEA000000 +#define MODEM_PHYS 0x04000000 +#define MODEM_VIRT 0xEB000000 +#define LATCH2_PHYS 0x08000000 +#define LATCH2_VIRT 0xEC000000 + +static struct map_desc ams_delta_io_desc[] __initdata = { + /* AMS_DELTA_LATCH1 */ + { + .virtual = LATCH1_VIRT, + .pfn = __phys_to_pfn(LATCH1_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + }, + /* AMS_DELTA_LATCH2 */ + { + .virtual = LATCH2_VIRT, + .pfn = __phys_to_pfn(LATCH2_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + }, + /* AMS_DELTA_MODEM */ + { + .virtual = MODEM_VIRT, + .pfn = __phys_to_pfn(MODEM_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + } +}; + +static const struct omap_lcd_config ams_delta_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static struct omap_usb_config ams_delta_usb_config __initdata = { + .register_host = 1, + .hmc_mode = 16, + .pins[0] = 2, +}; + +#define LATCH1_NGPIO 8 + +static struct resource latch1_resources[] = { + [0] = { + .name = "dat", + .start = LATCH1_PHYS, + .end = LATCH1_PHYS + (LATCH1_NGPIO - 1) / 8, + .flags = IORESOURCE_MEM, + }, +}; + +#define LATCH1_LABEL "latch1" + +static struct bgpio_pdata latch1_pdata = { + .label = LATCH1_LABEL, + .base = -1, + .ngpio = LATCH1_NGPIO, +}; + +static struct platform_device latch1_gpio_device = { + .name = "basic-mmio-gpio", + .id = 0, + .resource = latch1_resources, + .num_resources = ARRAY_SIZE(latch1_resources), + .dev = { + .platform_data = &latch1_pdata, + }, +}; + +#define LATCH1_PIN_LED_CAMERA 0 +#define LATCH1_PIN_LED_ADVERT 1 +#define LATCH1_PIN_LED_MAIL 2 +#define LATCH1_PIN_LED_HANDSFREE 3 +#define LATCH1_PIN_LED_VOICEMAIL 4 +#define LATCH1_PIN_LED_VOICE 5 +#define LATCH1_PIN_DOCKIT1 6 +#define LATCH1_PIN_DOCKIT2 7 + +#define LATCH2_NGPIO 16 + +static struct resource latch2_resources[] = { + [0] = { + .name = "dat", + .start = LATCH2_PHYS, + .end = LATCH2_PHYS + (LATCH2_NGPIO - 1) / 8, + .flags = IORESOURCE_MEM, + }, +}; + +#define LATCH2_LABEL "latch2" + +static struct bgpio_pdata latch2_pdata = { + .label = LATCH2_LABEL, + .base = -1, + .ngpio = LATCH2_NGPIO, +}; + +static struct platform_device latch2_gpio_device = { + .name = "basic-mmio-gpio", + .id = 1, + .resource = latch2_resources, + .num_resources = ARRAY_SIZE(latch2_resources), + .dev = { + .platform_data = &latch2_pdata, + }, +}; + +#define LATCH2_PIN_LCD_VBLEN 0 +#define LATCH2_PIN_LCD_NDISP 1 +#define LATCH2_PIN_NAND_NCE 2 +#define LATCH2_PIN_NAND_NRE 3 +#define LATCH2_PIN_NAND_NWP 4 +#define LATCH2_PIN_NAND_NWE 5 +#define LATCH2_PIN_NAND_ALE 6 +#define LATCH2_PIN_NAND_CLE 7 +#define LATCH2_PIN_KEYBRD_PWR 8 +#define LATCH2_PIN_KEYBRD_DATAOUT 9 +#define LATCH2_PIN_SCARD_RSTIN 10 +#define LATCH2_PIN_SCARD_CMDVCC 11 +#define LATCH2_PIN_MODEM_NRESET 12 +#define LATCH2_PIN_MODEM_CODEC 13 +#define LATCH2_PIN_HANDSFREE_MUTE 14 +#define LATCH2_PIN_HANDSET_MUTE 15 + +static struct regulator_consumer_supply modem_nreset_consumers[] = { + REGULATOR_SUPPLY("RESET#", "serial8250.1"), + REGULATOR_SUPPLY("POR", "cx20442-codec"), +}; + +static struct regulator_init_data modem_nreset_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(modem_nreset_consumers), + .consumer_supplies = modem_nreset_consumers, +}; + +static struct fixed_voltage_config modem_nreset_config = { + .supply_name = "modem_nreset", + .microvolts = 3300000, + .startup_delay = 25000, + .enabled_at_boot = 1, + .init_data = &modem_nreset_data, +}; + +static struct platform_device modem_nreset_device = { + .name = "reg-fixed-voltage", + .id = -1, + .dev = { + .platform_data = &modem_nreset_config, + }, +}; + +static struct gpiod_lookup_table ams_delta_nreset_gpiod_table = { + .dev_id = "reg-fixed-voltage", + .table = { + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_NRESET, + NULL, GPIO_ACTIVE_HIGH), + { }, + }, +}; + +struct modem_private_data { + struct regulator *regulator; +}; + +static struct modem_private_data modem_priv; + +/* + * Define partitions for flash device + */ + +static struct mtd_partition partition_info[] = { + { .name = "Kernel", + .offset = 0, + .size = 3 * SZ_1M + SZ_512K }, + { .name = "u-boot", + .offset = 3 * SZ_1M + SZ_512K, + .size = SZ_256K }, + { .name = "u-boot params", + .offset = 3 * SZ_1M + SZ_512K + SZ_256K, + .size = SZ_256K }, + { .name = "Amstrad LDR", + .offset = 4 * SZ_1M, + .size = SZ_256K }, + { .name = "File system", + .offset = 4 * SZ_1M + 1 * SZ_256K, + .size = 27 * SZ_1M }, + { .name = "PBL reserved", + .offset = 32 * SZ_1M - 3 * SZ_256K, + .size = 3 * SZ_256K }, +}; + +static struct gpio_nand_platdata nand_platdata = { + .parts = partition_info, + .num_parts = ARRAY_SIZE(partition_info), +}; + +static struct platform_device ams_delta_nand_device = { + .name = "ams-delta-nand", + .id = -1, + .dev = { + .platform_data = &nand_platdata, + }, +}; + +#define OMAP_GPIO_LABEL "gpio-0-15" +#define OMAP_MPUIO_LABEL "mpuio" + +static struct gpiod_lookup_table ams_delta_nand_gpio_table = { + .table = { + GPIO_LOOKUP(OMAP_GPIO_LABEL, AMS_DELTA_GPIO_PIN_NAND_RB, "rdy", + 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0), + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0), + { }, + }, +}; + +static struct resource ams_delta_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data ams_delta_keymap_data = { + .keymap = ams_delta_keymap, + .keymap_size = ARRAY_SIZE(ams_delta_keymap), +}; + +static struct omap_kp_platform_data ams_delta_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &ams_delta_keymap_data, + .delay = 9, +}; + +static struct platform_device ams_delta_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &ams_delta_kp_data, + }, + .num_resources = ARRAY_SIZE(ams_delta_kp_resources), + .resource = ams_delta_kp_resources, +}; + +static struct platform_device ams_delta_lcd_device = { + .name = "lcd_ams_delta", + .id = -1, +}; + +static struct gpiod_lookup_table ams_delta_lcd_gpio_table = { + .table = { + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_LCD_VBLEN, "vblen", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_LCD_NDISP, "ndisp", 0), + { }, + }, +}; + +static struct gpio_led gpio_leds[] __initdata = { + [LATCH1_PIN_LED_CAMERA] = { + .name = "camera", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + [LATCH1_PIN_LED_ADVERT] = { + .name = "advert", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + [LATCH1_PIN_LED_MAIL] = { + .name = "email", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + [LATCH1_PIN_LED_HANDSFREE] = { + .name = "handsfree", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + [LATCH1_PIN_LED_VOICEMAIL] = { + .name = "voicemail", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, + [LATCH1_PIN_LED_VOICE] = { + .name = "voice", + .default_state = LEDS_GPIO_DEFSTATE_OFF, + }, +}; + +static const struct gpio_led_platform_data leds_pdata __initconst = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct gpiod_lookup_table leds_gpio_table = { + .table = { + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_CAMERA, NULL, + LATCH1_PIN_LED_CAMERA, 0), + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_ADVERT, NULL, + LATCH1_PIN_LED_ADVERT, 0), + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_MAIL, NULL, + LATCH1_PIN_LED_MAIL, 0), + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_HANDSFREE, NULL, + LATCH1_PIN_LED_HANDSFREE, 0), + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_VOICEMAIL, NULL, + LATCH1_PIN_LED_VOICEMAIL, 0), + GPIO_LOOKUP_IDX(LATCH1_LABEL, LATCH1_PIN_LED_VOICE, NULL, + LATCH1_PIN_LED_VOICE, 0), + { }, + }, +}; + +static struct platform_device ams_delta_audio_device = { + .name = "ams-delta-audio", + .id = -1, +}; + +static struct gpiod_lookup_table ams_delta_audio_gpio_table = { + .table = { + GPIO_LOOKUP(OMAP_GPIO_LABEL, AMS_DELTA_GPIO_PIN_HOOK_SWITCH, + "hook_switch", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_CODEC, + "modem_codec", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSFREE_MUTE, + "handsfree_mute", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSET_MUTE, + "handset_mute", 0), + { }, + }, +}; + +static struct platform_device cx20442_codec_device = { + .name = "cx20442-codec", + .id = -1, +}; + +static struct resource ams_delta_serio_resources[] = { + { + .flags = IORESOURCE_IRQ, + /* + * Initialize IRQ resource with invalid IRQ number. + * It will be replaced with dynamically allocated GPIO IRQ + * obtained from GPIO chip as soon as the chip is available. + */ + .start = -EINVAL, + .end = -EINVAL, + }, +}; + +static struct platform_device ams_delta_serio_device = { + .name = "ams-delta-serio", + .id = PLATFORM_DEVID_NONE, + .dev = { + /* + * Initialize .platform_data explicitly with NULL to + * indicate it is going to be used. It will be replaced + * with FIQ buffer address as soon as FIQ is initialized. + */ + .platform_data = NULL, + }, + .num_resources = ARRAY_SIZE(ams_delta_serio_resources), + .resource = ams_delta_serio_resources, +}; + +static struct regulator_consumer_supply keybrd_pwr_consumers[] = { + /* + * Initialize supply .dev_name with NULL. It will be replaced + * with serio dev_name() as soon as the serio device is registered. + */ + REGULATOR_SUPPLY("vcc", NULL), +}; + +static struct regulator_init_data keybrd_pwr_initdata = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(keybrd_pwr_consumers), + .consumer_supplies = keybrd_pwr_consumers, +}; + +static struct fixed_voltage_config keybrd_pwr_config = { + .supply_name = "keybrd_pwr", + .microvolts = 5000000, + .init_data = &keybrd_pwr_initdata, +}; + +static struct platform_device keybrd_pwr_device = { + .name = "reg-fixed-voltage", + .id = PLATFORM_DEVID_AUTO, + .dev = { + .platform_data = &keybrd_pwr_config, + }, +}; + +static struct gpiod_lookup_table keybrd_pwr_gpio_table = { + .table = { + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_KEYBRD_PWR, NULL, + GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static struct platform_device *ams_delta_devices[] __initdata = { + &latch1_gpio_device, + &latch2_gpio_device, + &ams_delta_kp_device, + &ams_delta_audio_device, + &ams_delta_serio_device, + &ams_delta_nand_device, + &ams_delta_lcd_device, + &cx20442_codec_device, +}; + +static struct gpiod_lookup_table *ams_delta_gpio_tables[] __initdata = { + &ams_delta_nreset_gpiod_table, + &ams_delta_audio_gpio_table, + &keybrd_pwr_gpio_table, + &ams_delta_lcd_gpio_table, + &ams_delta_nand_gpio_table, +}; + +/* + * Some drivers may not use GPIO lookup tables but need to be provided + * with GPIO numbers. The same applies to GPIO based IRQ lines - some + * drivers may even not use GPIO layer but expect just IRQ numbers. + * We could either define GPIO lookup tables then use them on behalf + * of those devices, or we can use GPIO driver level methods for + * identification of GPIO and IRQ numbers. For the purpose of the latter, + * defina a helper function which identifies GPIO chips by their labels. + */ +static int gpiochip_match_by_label(struct gpio_chip *chip, void *data) +{ + char *label = data; + + return !strcmp(label, chip->label); +} + +static struct gpiod_hog ams_delta_gpio_hogs[] = { + GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout", + GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW), + {}, +}; + +static struct plat_serial8250_port ams_delta_modem_ports[]; + +/* + * Obtain MODEM IRQ GPIO descriptor using its hardware pin + * number and assign related IRQ number to the MODEM port. + * Keep the GPIO descriptor open so nobody steps in. + */ +static void __init modem_assign_irq(struct gpio_chip *chip) +{ + struct gpio_desc *gpiod; + + gpiod = gpiochip_request_own_desc(chip, AMS_DELTA_GPIO_PIN_MODEM_IRQ, + "modem_irq", GPIO_ACTIVE_HIGH, + GPIOD_IN); + if (IS_ERR(gpiod)) { + pr_err("%s: modem IRQ GPIO request failed (%ld)\n", __func__, + PTR_ERR(gpiod)); + } else { + ams_delta_modem_ports[0].irq = gpiod_to_irq(gpiod); + } +} + +/* + * The purpose of this function is to take care of proper initialization of + * devices and data structures which depend on GPIO lines provided by OMAP GPIO + * banks but their drivers don't use GPIO lookup tables or GPIO layer at all. + * The function may be called as soon as OMAP GPIO devices are probed. + * Since that happens at postcore_initcall, it can be called successfully + * from init_machine or later. + * Dependent devices may be registered from within this function or later. + */ +static void __init omap_gpio_deps_init(void) +{ + struct gpio_chip *chip; + + chip = gpiochip_find(OMAP_GPIO_LABEL, gpiochip_match_by_label); + if (!chip) { + pr_err("%s: OMAP GPIO chip not found\n", __func__); + return; + } + + /* + * Start with FIQ initialization as it may have to request + * and release successfully each OMAP GPIO pin in turn. + */ + ams_delta_init_fiq(chip, &ams_delta_serio_device); + + modem_assign_irq(chip); +} + +/* + * Initialize latch2 pins with values which are safe for dependent on-board + * devices or useful for their successull initialization even before GPIO + * driver takes control over the latch pins: + * - LATCH2_PIN_LCD_VBLEN = 0 + * - LATCH2_PIN_LCD_NDISP = 0 Keep LCD device powered off before its + * driver takes control over it. + * - LATCH2_PIN_NAND_NCE = 0 + * - LATCH2_PIN_NAND_NWP = 0 Keep NAND device down and write- + * protected before its driver takes + * control over it. + * - LATCH2_PIN_KEYBRD_PWR = 0 Keep keyboard powered off before serio + * driver takes control over it. + * - LATCH2_PIN_KEYBRD_DATAOUT = 0 Keep low to avoid corruption of first + * byte of data received from attached + * keyboard when serio device is probed; + * the pin is also hogged low by the latch2 + * GPIO driver as soon as it is ready. + * - LATCH2_PIN_MODEM_NRESET = 1 Enable voice MODEM device, allowing for + * its successful probe even before a + * regulator it depends on, which in turn + * takes control over the pin, is set up. + * - LATCH2_PIN_MODEM_CODEC = 1 Attach voice MODEM CODEC data port + * to the MODEM so the CODEC is under + * control even if audio driver doesn't + * take it over. + */ +static void __init ams_delta_latch2_init(void) +{ + u16 latch2 = 1 << LATCH2_PIN_MODEM_NRESET | 1 << LATCH2_PIN_MODEM_CODEC; + + __raw_writew(latch2, IOMEM(LATCH2_VIRT)); +} + +static void __init ams_delta_init(void) +{ + struct platform_device *leds_pdev; + + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + + /* parallel camera interface */ + omap_cfg_reg(H19_1610_CAM_EXCLK); + omap_cfg_reg(J15_1610_CAM_LCLK); + omap_cfg_reg(L18_1610_CAM_VS); + omap_cfg_reg(L15_1610_CAM_HS); + omap_cfg_reg(L19_1610_CAM_D0); + omap_cfg_reg(K14_1610_CAM_D1); + omap_cfg_reg(K15_1610_CAM_D2); + omap_cfg_reg(K19_1610_CAM_D3); + omap_cfg_reg(K18_1610_CAM_D4); + omap_cfg_reg(J14_1610_CAM_D5); + omap_cfg_reg(J19_1610_CAM_D6); + omap_cfg_reg(J18_1610_CAM_D7); + + omap_gpio_deps_init(); + ams_delta_latch2_init(); + gpiod_add_hogs(ams_delta_gpio_hogs); + + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + + omap1_usb_init(&ams_delta_usb_config); + platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices)); + + /* + * As soon as regulator consumers have been registered, assign their + * dev_names to consumer supply entries of respective regulators. + */ + keybrd_pwr_consumers[0].dev_name = + dev_name(&ams_delta_serio_device.dev); + + /* + * Once consumer supply entries are populated with dev_names, + * register regulator devices. At this stage only the keyboard + * power regulator has its consumer supply table fully populated. + */ + platform_device_register(&keybrd_pwr_device); + + /* + * As soon as GPIO consumers have been registered, assign + * their dev_names to respective GPIO lookup tables. + */ + ams_delta_audio_gpio_table.dev_id = + dev_name(&ams_delta_audio_device.dev); + keybrd_pwr_gpio_table.dev_id = dev_name(&keybrd_pwr_device.dev); + ams_delta_nand_gpio_table.dev_id = dev_name(&ams_delta_nand_device.dev); + ams_delta_lcd_gpio_table.dev_id = dev_name(&ams_delta_lcd_device.dev); + + /* + * Once GPIO lookup tables are populated with dev_names, register them. + */ + gpiod_add_lookup_tables(ams_delta_gpio_tables, + ARRAY_SIZE(ams_delta_gpio_tables)); + + leds_pdev = gpio_led_register_device(PLATFORM_DEVID_NONE, &leds_pdata); + if (!IS_ERR_OR_NULL(leds_pdev)) { + leds_gpio_table.dev_id = dev_name(&leds_pdev->dev); + gpiod_add_lookup_table(&leds_gpio_table); + } + + omap_writew(omap_readw(ARM_RSTCT1) | 0x0004, ARM_RSTCT1); + + omapfb_set_lcd_config(&ams_delta_lcd_config); +} + +static void modem_pm(struct uart_port *port, unsigned int state, unsigned old) +{ + struct modem_private_data *priv = port->private_data; + int ret; + + if (!priv) + return; + + if (IS_ERR(priv->regulator)) + return; + + if (state == old) + return; + + if (state == 0) + ret = regulator_enable(priv->regulator); + else if (old == 0) + ret = regulator_disable(priv->regulator); + else + ret = 0; + + if (ret) + dev_warn(port->dev, + "ams_delta modem_pm: failed to %sable regulator: %d\n", + state ? "dis" : "en", ret); +} + +static struct plat_serial8250_port ams_delta_modem_ports[] = { + { + .membase = IOMEM(MODEM_VIRT), + .mapbase = MODEM_PHYS, + .irq = IRQ_NOTCONNECTED, /* changed later */ + .flags = UPF_BOOT_AUTOCONF, + .irqflags = IRQF_TRIGGER_RISING, + .iotype = UPIO_MEM, + .regshift = 1, + .uartclk = BASE_BAUD * 16, + .pm = modem_pm, + .private_data = &modem_priv, + }, + { }, +}; + +static struct platform_device ams_delta_modem_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM1, + .dev = { + .platform_data = ams_delta_modem_ports, + }, +}; + +static int __init modem_nreset_init(void) +{ + int err; + + err = platform_device_register(&modem_nreset_device); + if (err) + pr_err("Couldn't register the modem regulator device\n"); + + return err; +} + + +/* + * This function expects MODEM IRQ number already assigned to the port. + * The MODEM device requires its RESET# pin kept high during probe. + * That requirement can be fulfilled in several ways: + * - with a descriptor of already functional modem_nreset regulator + * assigned to the MODEM private data, + * - with the regulator not yet controlled by modem_pm function but + * already enabled by default on probe, + * - before the modem_nreset regulator is probed, with the pin already + * set high explicitly. + * The last one is already guaranteed by ams_delta_latch2_init() called + * from machine_init. + * In order to avoid taking over ttyS0 device slot, the MODEM device + * should be registered after OMAP serial ports. Since those ports + * are registered at arch_initcall, this function can be called safely + * at arch_initcall_sync earliest. + */ +static int __init ams_delta_modem_init(void) +{ + int err; + + if (!machine_is_ams_delta()) + return -ENODEV; + + omap_cfg_reg(M14_1510_GPIO2); + + /* Initialize the modem_nreset regulator consumer before use */ + modem_priv.regulator = ERR_PTR(-ENODEV); + + err = platform_device_register(&ams_delta_modem_device); + + return err; +} +arch_initcall_sync(ams_delta_modem_init); + +static int __init late_init(void) +{ + int err; + + err = modem_nreset_init(); + if (err) + return err; + + /* + * Once the modem device is registered, the modem_nreset + * regulator can be requested on behalf of that device. + */ + modem_priv.regulator = regulator_get(&ams_delta_modem_device.dev, + "RESET#"); + if (IS_ERR(modem_priv.regulator)) { + err = PTR_ERR(modem_priv.regulator); + goto unregister; + } + return 0; + +unregister: + platform_device_unregister(&ams_delta_modem_device); + return err; +} + +static void __init ams_delta_init_late(void) +{ + omap1_init_late(); + late_init(); +} + +static void __init ams_delta_map_io(void) +{ + omap15xx_map_io(); + iotable_init(ams_delta_io_desc, ARRAY_SIZE(ams_delta_io_desc)); +} + +MACHINE_START(AMS_DELTA, "Amstrad E3 (Delta)") + /* Maintainer: Jonathan McDowell <noodles@earth.li> */ + .atag_offset = 0x100, + .map_io = ams_delta_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = ams_delta_init, + .init_late = ams_delta_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-ams-delta.h b/arch/arm/mach-omap1/board-ams-delta.h new file mode 100644 index 000000000..b5c4a373b --- /dev/null +++ b/arch/arm/mach-omap1/board-ams-delta.h @@ -0,0 +1,42 @@ +/* + * arch/arm/mach-omap1/board-ams-delta.h + * + * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __ASM_ARCH_OMAP_AMS_DELTA_H +#define __ASM_ARCH_OMAP_AMS_DELTA_H + +#if defined (CONFIG_MACH_AMS_DELTA) + +#define AMS_DELTA_GPIO_PIN_KEYBRD_DATA 0 +#define AMS_DELTA_GPIO_PIN_KEYBRD_CLK 1 +#define AMS_DELTA_GPIO_PIN_MODEM_IRQ 2 +#define AMS_DELTA_GPIO_PIN_HOOK_SWITCH 4 +#define AMS_DELTA_GPIO_PIN_SCARD_NOFF 6 +#define AMS_DELTA_GPIO_PIN_SCARD_IO 7 +#define AMS_DELTA_GPIO_PIN_CONFIG 11 +#define AMS_DELTA_GPIO_PIN_NAND_RB 12 + +#endif /* CONFIG_MACH_AMS_DELTA */ + +#endif /* __ASM_ARCH_OMAP_AMS_DELTA_H */ diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c new file mode 100644 index 000000000..f21e15c7b --- /dev/null +++ b/arch/arm/mach-omap1/board-fsample.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-fsample.c + * + * Modified from board-perseus2.c + * + * Original OMAP730 support by Jean Pihet <j-pihet@ti.com> + * Updated for 2.6 by Kevin Hilman <kjh@hilman.org> + */ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/platnand.h> +#include <linux/mtd/physmap.h> +#include <linux/input.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <linux/soc/ti/omap1-io.h> +#include <linux/platform_data/keypad-omap.h> +#include "tc.h" + +#include "mux.h" +#include "flash.h" +#include "hardware.h" +#include "iomap.h" +#include "common.h" +#include "fpga.h" + +/* fsample is pretty close to p2-sample */ + +#define fsample_cpld_read(reg) __raw_readb(reg) +#define fsample_cpld_write(val, reg) __raw_writeb(val, reg) + +#define FSAMPLE_CPLD_BASE 0xE8100000 +#define FSAMPLE_CPLD_SIZE SZ_4K +#define FSAMPLE_CPLD_START 0x05080000 + +#define FSAMPLE_CPLD_REG_A (FSAMPLE_CPLD_BASE + 0x00) +#define FSAMPLE_CPLD_SWITCH (FSAMPLE_CPLD_BASE + 0x02) +#define FSAMPLE_CPLD_UART (FSAMPLE_CPLD_BASE + 0x02) +#define FSAMPLE_CPLD_REG_B (FSAMPLE_CPLD_BASE + 0x04) +#define FSAMPLE_CPLD_VERSION (FSAMPLE_CPLD_BASE + 0x06) +#define FSAMPLE_CPLD_SET_CLR (FSAMPLE_CPLD_BASE + 0x06) + +#define FSAMPLE_CPLD_BIT_BT_RESET 0 +#define FSAMPLE_CPLD_BIT_LCD_RESET 1 +#define FSAMPLE_CPLD_BIT_CAM_PWDN 2 +#define FSAMPLE_CPLD_BIT_CHARGER_ENABLE 3 +#define FSAMPLE_CPLD_BIT_SD_MMC_EN 4 +#define FSAMPLE_CPLD_BIT_aGPS_PWREN 5 +#define FSAMPLE_CPLD_BIT_BACKLIGHT 6 +#define FSAMPLE_CPLD_BIT_aGPS_EN_RESET 7 +#define FSAMPLE_CPLD_BIT_aGPS_SLEEPx_N 8 +#define FSAMPLE_CPLD_BIT_OTG_RESET 9 + +#define fsample_cpld_set(bit) \ + fsample_cpld_write((((bit) & 15) << 4) | 0x0f, FSAMPLE_CPLD_SET_CLR) + +#define fsample_cpld_clear(bit) \ + fsample_cpld_write(0xf0 | ((bit) & 15), FSAMPLE_CPLD_SET_CLR) + +static const unsigned int fsample_keymap[] = { + KEY(0, 0, KEY_UP), + KEY(1, 0, KEY_RIGHT), + KEY(2, 0, KEY_LEFT), + KEY(3, 0, KEY_DOWN), + KEY(4, 0, KEY_ENTER), + KEY(0, 1, KEY_F10), + KEY(1, 1, KEY_SEND), + KEY(2, 1, KEY_END), + KEY(3, 1, KEY_VOLUMEDOWN), + KEY(4, 1, KEY_VOLUMEUP), + KEY(5, 1, KEY_RECORD), + KEY(0, 2, KEY_F9), + KEY(1, 2, KEY_3), + KEY(2, 2, KEY_6), + KEY(3, 2, KEY_9), + KEY(4, 2, KEY_KPDOT), + KEY(0, 3, KEY_BACK), + KEY(1, 3, KEY_2), + KEY(2, 3, KEY_5), + KEY(3, 3, KEY_8), + KEY(4, 3, KEY_0), + KEY(5, 3, KEY_KPSLASH), + KEY(0, 4, KEY_HOME), + KEY(1, 4, KEY_1), + KEY(2, 4, KEY_4), + KEY(3, 4, KEY_7), + KEY(4, 4, KEY_KPASTERISK), + KEY(5, 4, KEY_POWER), +}; + +static struct smc91x_platdata smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ + .end = H2P2_DBG_FPGA_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_7XX_MPU_EXT_NIRQ, + .end = 0, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + }, +}; + +static void __init fsample_init_smc91x(void) +{ + __raw_writeb(1, H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); + __raw_writeb(__raw_readb(H2P2_DBG_FPGA_LAN_RESET) & ~1, + H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); +} + +static struct mtd_partition nor_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* rest of flash is a file system */ + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + }, +}; + +static struct physmap_flash_data nor_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), +}; + +static struct resource nor_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nor_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &nor_data, + }, + .num_resources = 1, + .resource = &nor_resource, +}; + +#define FSAMPLE_NAND_RB_GPIO_PIN 62 + +static int nand_dev_ready(struct nand_chip *chip) +{ + return gpio_get_value(FSAMPLE_NAND_RB_GPIO_PIN); +} + +static struct platform_nand_data nand_data = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .options = NAND_SAMSUNG_LP_OPTIONS, + }, + .ctrl = { + .cmd_ctrl = omap1_nand_cmd_ctl, + .dev_ready = nand_dev_ready, + }, +}; + +static struct resource nand_resource = { + .start = OMAP_CS3_PHYS, + .end = OMAP_CS3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "gen_nand", + .id = 0, + .dev = { + .platform_data = &nand_data, + }, + .num_resources = 1, + .resource = &nand_resource, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &smc91x_info, + }, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct resource kp_resources[] = { + [0] = { + .start = INT_7XX_MPUIO_KEYPAD, + .end = INT_7XX_MPUIO_KEYPAD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data fsample_keymap_data = { + .keymap = fsample_keymap, + .keymap_size = ARRAY_SIZE(fsample_keymap), +}; + +static struct omap_kp_platform_data kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &fsample_keymap_data, + .delay = 4, +}; + +static struct platform_device kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &kp_data, + }, + .num_resources = ARRAY_SIZE(kp_resources), + .resource = kp_resources, +}; + +static struct platform_device *devices[] __initdata = { + &nor_device, + &nand_device, + &smc91x_device, + &kp_device, +}; + +static const struct omap_lcd_config fsample_lcd_config = { + .ctrl_name = "internal", +}; + +static void __init omap_fsample_init(void) +{ + /* Early, board-dependent init */ + + /* + * Hold GSM Reset until needed + */ + omap_writew(omap_readw(OMAP7XX_DSP_M_CTL) & ~1, OMAP7XX_DSP_M_CTL); + + /* + * UARTs -> done automagically by 8250 driver + */ + + /* + * CSx timings, GPIO Mux ... setup + */ + + /* Flash: CS0 timings setup */ + omap_writel(0x0000fff3, OMAP7XX_FLASH_CFG_0); + omap_writel(0x00000088, OMAP7XX_FLASH_ACFG_0); + + /* + * Ethernet support through the debug board + * CS1 timings setup + */ + omap_writel(0x0000fff3, OMAP7XX_FLASH_CFG_1); + omap_writel(0x00000000, OMAP7XX_FLASH_ACFG_1); + + /* + * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, + * It is used as the Ethernet controller interrupt + */ + omap_writel(omap_readl(OMAP7XX_IO_CONF_9) & 0x1FFFFFFF, + OMAP7XX_IO_CONF_9); + + fsample_init_smc91x(); + + BUG_ON(gpio_request(FSAMPLE_NAND_RB_GPIO_PIN, "NAND ready") < 0); + gpio_direction_input(FSAMPLE_NAND_RB_GPIO_PIN); + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); + + /* Mux pins for keypad */ + omap_cfg_reg(E2_7XX_KBR0); + omap_cfg_reg(J7_7XX_KBR1); + omap_cfg_reg(E1_7XX_KBR2); + omap_cfg_reg(F3_7XX_KBR3); + omap_cfg_reg(D2_7XX_KBR4); + omap_cfg_reg(C2_7XX_KBC0); + omap_cfg_reg(D3_7XX_KBC1); + omap_cfg_reg(E4_7XX_KBC2); + omap_cfg_reg(F4_7XX_KBC3); + omap_cfg_reg(E3_7XX_KBC4); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&fsample_lcd_config); +} + +/* Only FPGA needs to be mapped here. All others are done with ioremap */ +static struct map_desc omap_fsample_io_desc[] __initdata = { + { + .virtual = H2P2_DBG_FPGA_BASE, + .pfn = __phys_to_pfn(H2P2_DBG_FPGA_START), + .length = H2P2_DBG_FPGA_SIZE, + .type = MT_DEVICE + }, + { + .virtual = FSAMPLE_CPLD_BASE, + .pfn = __phys_to_pfn(FSAMPLE_CPLD_START), + .length = FSAMPLE_CPLD_SIZE, + .type = MT_DEVICE + } +}; + +static void __init omap_fsample_map_io(void) +{ + omap15xx_map_io(); + iotable_init(omap_fsample_io_desc, + ARRAY_SIZE(omap_fsample_io_desc)); +} + +MACHINE_START(OMAP_FSAMPLE, "OMAP730 F-Sample") +/* Maintainer: Brian Swetland <swetland@google.com> */ + .atag_offset = 0x100, + .map_io = omap_fsample_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_fsample_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-generic.c b/arch/arm/mach-omap1/board-generic.c new file mode 100644 index 000000000..3b2bcaf4b --- /dev/null +++ b/arch/arm/mach-omap1/board-generic.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-generic.c + * + * Modified from board-innovator1510.c + * + * Code for generic OMAP board. Should work on many OMAP systems where + * the device drivers take care of all the necessary hardware initialization. + * Do not put any board specific code to this file; create a new machine + * type if you need custom low-level initializations. + */ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "hardware.h" +#include "mux.h" +#include "usb.h" +#include "common.h" + +/* assume no Mini-AB port */ + +#ifdef CONFIG_ARCH_OMAP15XX +static struct omap_usb_config generic1510_usb_config __initdata = { + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 3, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct omap_usb_config generic1610_usb_config __initdata = { +#ifdef CONFIG_USB_OTG + .otg = 1, +#endif + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 6, +}; +#endif + +static void __init omap_generic_init(void) +{ +#ifdef CONFIG_ARCH_OMAP15XX + if (cpu_is_omap15xx()) { + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + omap1_usb_init(&generic1510_usb_config); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (!cpu_is_omap1510()) { + omap1_usb_init(&generic1610_usb_config); + } +#endif + + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); +} + +MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710") + /* Maintainer: Tony Lindgren <tony@atomide.com> */ + .atag_offset = 0x100, + .map_io = omap16xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_generic_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c new file mode 100644 index 000000000..06c540407 --- /dev/null +++ b/arch/arm/mach-omap1/board-h2-mmc.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-h2-mmc.c + * + * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT + * Author: Felipe Balbi <felipe.lima@indt.org.br> + * + * This code is based on linux/arch/arm/mach-omap2/board-n800-mmc.c, which is: + * Copyright (C) 2006 Nokia Corporation + */ +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/mfd/tps65010.h> + +#include "board-h2.h" +#include "mmc.h" + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static int mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + gpio_set_value(H2_TPS_GPIO_MMC_PWR_EN, power_on); + return 0; +} + +static int mmc_late_init(struct device *dev) +{ + int ret = gpio_request(H2_TPS_GPIO_MMC_PWR_EN, "MMC power"); + if (ret < 0) + return ret; + + gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 0); + + return ret; +} + +static void mmc_cleanup(struct device *dev) +{ + gpio_free(H2_TPS_GPIO_MMC_PWR_EN); +} + +/* + * H2 could use the following functions tested: + * - mmc_get_cover_state that uses OMAP_MPUIO(1) + * - mmc_get_wp that uses OMAP_MPUIO(3) + */ +static struct omap_mmc_platform_data mmc1_data = { + .nr_slots = 1, + .init = mmc_late_init, + .cleanup = mmc_cleanup, + .slots[0] = { + .set_power = mmc_set_power, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC]; + +void __init h2_mmc_init(void) +{ + mmc_data[0] = &mmc1_data; + omap1_init_mmc(mmc_data, OMAP16XX_NR_MMC); +} + +#else + +void __init h2_mmc_init(void) +{ +} + +#endif diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c new file mode 100644 index 000000000..f28a4c3ea --- /dev/null +++ b/arch/arm/mach-omap1/board-h2.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-h2.c + * + * Board specific inits for OMAP-1610 H2 + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> + * + * H2 specific changes and cleanup + * Copyright (C) 2004 Nokia Corporation by Imre Deak <imre.deak@nokia.com> + */ +#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/platnand.h> +#include <linux/mtd/physmap.h> +#include <linux/input.h> +#include <linux/mfd/tps65010.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> +#include <linux/omap-dma.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/leds.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "mux.h" +#include "flash.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" +#include "board-h2.h" + +/* The first 16 SoC GPIO lines are on this GPIO chip */ +#define OMAP_GPIO_LABEL "gpio-0-15" + +/* At OMAP1610 Innovator the Ethernet is directly connected to CS1 */ +#define OMAP1610_ETHR_START 0x04000300 + +static const unsigned int h2_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(1, 0, KEY_RIGHT), + KEY(2, 0, KEY_3), + KEY(3, 0, KEY_F10), + KEY(4, 0, KEY_F5), + KEY(5, 0, KEY_9), + KEY(0, 1, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(2, 1, KEY_2), + KEY(3, 1, KEY_F9), + KEY(4, 1, KEY_F7), + KEY(5, 1, KEY_0), + KEY(0, 2, KEY_ENTER), + KEY(1, 2, KEY_6), + KEY(2, 2, KEY_1), + KEY(3, 2, KEY_F2), + KEY(4, 2, KEY_F6), + KEY(5, 2, KEY_HOME), + KEY(0, 3, KEY_8), + KEY(1, 3, KEY_5), + KEY(2, 3, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(4, 3, KEY_F8), + KEY(5, 3, KEY_END), + KEY(0, 4, KEY_7), + KEY(1, 4, KEY_4), + KEY(2, 4, KEY_F11), + KEY(3, 4, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(5, 4, KEY_ESC), + KEY(0, 5, KEY_F13), + KEY(1, 5, KEY_F14), + KEY(2, 5, KEY_F15), + KEY(3, 5, KEY_F16), + KEY(4, 5, KEY_SLEEP), +}; + +static struct mtd_partition h2_nor_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data h2_nor_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = h2_nor_partitions, + .nr_parts = ARRAY_SIZE(h2_nor_partitions), +}; + +static struct resource h2_nor_resource = { + /* This is on CS3, wherever it's mapped */ + .flags = IORESOURCE_MEM, +}; + +static struct platform_device h2_nor_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &h2_nor_data, + }, + .num_resources = 1, + .resource = &h2_nor_resource, +}; + +static struct mtd_partition h2_nand_partitions[] = { +#if 0 + /* REVISIT: enable these partitions if you make NAND BOOT + * work on your H2 (rev C or newer); published versions of + * x-load only support P2 and H3. + */ + { + .name = "xloader", + .offset = 0, + .size = 64 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "bootloader", + .offset = MTDPART_OFS_APPEND, + .size = 256 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 192 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 2 * SZ_1M, + }, +#endif + { + .name = "filesystem", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + }, +}; + +#define H2_NAND_RB_GPIO_PIN 62 + +static int h2_nand_dev_ready(struct nand_chip *chip) +{ + return gpio_get_value(H2_NAND_RB_GPIO_PIN); +} + +static struct platform_nand_data h2_nand_platdata = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .nr_partitions = ARRAY_SIZE(h2_nand_partitions), + .partitions = h2_nand_partitions, + .options = NAND_SAMSUNG_LP_OPTIONS, + }, + .ctrl = { + .cmd_ctrl = omap1_nand_cmd_ctl, + .dev_ready = h2_nand_dev_ready, + }, +}; + +static struct resource h2_nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device h2_nand_device = { + .name = "gen_nand", + .id = 0, + .dev = { + .platform_data = &h2_nand_platdata, + }, + .num_resources = 1, + .resource = &h2_nand_resource, +}; + +static struct smc91x_platdata h2_smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +static struct resource h2_smc91x_resources[] = { + [0] = { + .start = OMAP1610_ETHR_START, /* Physical */ + .end = OMAP1610_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + }, +}; + +static struct platform_device h2_smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &h2_smc91x_info, + }, + .num_resources = ARRAY_SIZE(h2_smc91x_resources), + .resource = h2_smc91x_resources, +}; + +static struct resource h2_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data h2_keymap_data = { + .keymap = h2_keymap, + .keymap_size = ARRAY_SIZE(h2_keymap), +}; + +static struct omap_kp_platform_data h2_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &h2_keymap_data, + .rep = true, + .delay = 9, + .dbounce = true, +}; + +static struct platform_device h2_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h2_kp_data, + }, + .num_resources = ARRAY_SIZE(h2_kp_resources), + .resource = h2_kp_resources, +}; + +static const struct gpio_led h2_gpio_led_pins[] = { + { + .name = "h2:red", + .default_trigger = "heartbeat", + .gpio = 3, + }, + { + .name = "h2:green", + .default_trigger = "cpu0", + .gpio = OMAP_MPUIO(4), + }, +}; + +static struct gpio_led_platform_data h2_gpio_led_data = { + .leds = h2_gpio_led_pins, + .num_leds = ARRAY_SIZE(h2_gpio_led_pins), +}; + +static struct platform_device h2_gpio_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &h2_gpio_led_data, + }, +}; + +static struct platform_device *h2_devices[] __initdata = { + &h2_nor_device, + &h2_nand_device, + &h2_smc91x_device, + &h2_kp_device, + &h2_gpio_leds, +}; + +static void __init h2_init_smc91x(void) +{ + if (gpio_request(0, "SMC91x irq") < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } +} + +static int tps_setup(struct i2c_client *client, void *context) +{ + if (!IS_BUILTIN(CONFIG_TPS65010)) + return -ENOSYS; + + tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V | + TPS_LDO1_ENABLE | TPS_VLDO1_3_0V); + + return 0; +} + +static struct tps65010_board tps_board = { + .base = H2_TPS_GPIO_BASE, + .outmask = 0x0f, + .setup = tps_setup, +}; + +static struct i2c_board_info __initdata h2_i2c_board_info[] = { + { + I2C_BOARD_INFO("tps65010", 0x48), + .platform_data = &tps_board, + }, { + .type = "isp1301_omap", + .addr = 0x2d, + .dev_name = "isp1301", + }, +}; + +static struct gpiod_lookup_table isp1301_gpiod_table = { + .dev_id = "isp1301", + .table = { + /* Active low since the irq triggers on falling edge */ + GPIO_LOOKUP(OMAP_GPIO_LABEL, 2, + NULL, GPIO_ACTIVE_LOW), + { }, + }, +}; + +static struct omap_usb_config h2_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#if IS_ENABLED(CONFIG_USB_OMAP) + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ + /* .hmc_mode = 21,*/ /* 0:host(off) 1:dev(loopback) 2:host(loopback) */ +#elif IS_ENABLED(CONFIG_USB_OHCI_HCD) + /* needs OTG cable, or NONSTANDARD (B-to-MiniB) */ + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ +#endif + + .pins[1] = 3, +}; + +static const struct omap_lcd_config h2_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static void __init h2_init(void) +{ + h2_init_smc91x(); + + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * FIXME revC boards (and H3) support NAND-boot, with a dip switch to + * put NOR on CS2B and NAND (which on H2 may be 16bit) on CS3. Try + * detecting that in code here, to avoid probing every possible flash + * configuration... + */ + h2_nor_resource.end = h2_nor_resource.start = omap_cs3_phys(); + h2_nor_resource.end += SZ_32M - 1; + + h2_nand_resource.end = h2_nand_resource.start = OMAP_CS2B_PHYS; + h2_nand_resource.end += SZ_4K - 1; + BUG_ON(gpio_request(H2_NAND_RB_GPIO_PIN, "NAND ready") < 0); + gpio_direction_input(H2_NAND_RB_GPIO_PIN); + + gpiod_add_lookup_table(&isp1301_gpiod_table); + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); + + /* MMC: card detect and WP */ + /* omap_cfg_reg(U19_ARMIO1); */ /* CD */ + omap_cfg_reg(BALLOUT_V8_ARMIO3); /* WP */ + + /* Mux pins for keypad */ + omap_cfg_reg(F18_1610_KBC0); + omap_cfg_reg(D20_1610_KBC1); + omap_cfg_reg(D19_1610_KBC2); + omap_cfg_reg(E18_1610_KBC3); + omap_cfg_reg(C21_1610_KBC4); + omap_cfg_reg(G18_1610_KBR0); + omap_cfg_reg(F19_1610_KBR1); + omap_cfg_reg(H14_1610_KBR2); + omap_cfg_reg(E20_1610_KBR3); + omap_cfg_reg(E19_1610_KBR4); + omap_cfg_reg(N19_1610_KBR5); + + /* GPIO based LEDs */ + omap_cfg_reg(P18_1610_GPIO3); + omap_cfg_reg(MPUIO4); + + h2_smc91x_resources[1].start = gpio_to_irq(0); + h2_smc91x_resources[1].end = gpio_to_irq(0); + platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); + omap_serial_init(); + + /* ISP1301 IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); + h2_i2c_board_info[0].irq = gpio_to_irq(58); + omap_register_i2c_bus(1, 100, h2_i2c_board_info, + ARRAY_SIZE(h2_i2c_board_info)); + omap1_usb_init(&h2_usb_config); + h2_mmc_init(); + + omapfb_set_lcd_config(&h2_lcd_config); +} + +MACHINE_START(OMAP_H2, "TI-H2") + /* Maintainer: Imre Deak <imre.deak@nokia.com> */ + .atag_offset = 0x100, + .map_io = omap16xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = h2_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-h2.h b/arch/arm/mach-omap1/board-h2.h new file mode 100644 index 000000000..315e26625 --- /dev/null +++ b/arch/arm/mach-omap1/board-h2.h @@ -0,0 +1,38 @@ +/* + * arch/arm/mach-omap1/board-h2.h + * + * Hardware definitions for TI OMAP1610 H2 board. + * + * Cleanup for Linux-2.6 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP_H2_H +#define __ASM_ARCH_OMAP_H2_H + +#define H2_TPS_GPIO_BASE (OMAP_MAX_GPIO_LINES + 16 /* MPUIO */) +# define H2_TPS_GPIO_MMC_PWR_EN (H2_TPS_GPIO_BASE + 3) + +extern void h2_mmc_init(void); + +#endif /* __ASM_ARCH_OMAP_H2_H */ + diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c new file mode 100644 index 000000000..f595bd4f5 --- /dev/null +++ b/arch/arm/mach-omap1/board-h3-mmc.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-h3-mmc.c + * + * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT + * Author: Felipe Balbi <felipe.lima@indt.org.br> + * + * This code is based on linux/arch/arm/mach-omap2/board-n800-mmc.c, which is: + * Copyright (C) 2006 Nokia Corporation + */ +#include <linux/gpio.h> +#include <linux/platform_device.h> + +#include <linux/mfd/tps65010.h> + +#include "common.h" +#include "board-h3.h" +#include "mmc.h" + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static int mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + gpio_set_value(H3_TPS_GPIO_MMC_PWR_EN, power_on); + return 0; +} + +/* + * H3 could use the following functions tested: + * - mmc_get_cover_state that uses OMAP_MPUIO(1) + * - mmc_get_wp that maybe uses OMAP_MPUIO(3) + */ +static struct omap_mmc_platform_data mmc1_data = { + .nr_slots = 1, + .slots[0] = { + .set_power = mmc_set_power, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC]; + +void __init h3_mmc_init(void) +{ + int ret; + + ret = gpio_request(H3_TPS_GPIO_MMC_PWR_EN, "MMC power"); + if (ret < 0) + return; + gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 0); + + mmc_data[0] = &mmc1_data; + omap1_init_mmc(mmc_data, OMAP16XX_NR_MMC); +} + +#else + +void __init h3_mmc_init(void) +{ +} + +#endif diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c new file mode 100644 index 000000000..1e4c57710 --- /dev/null +++ b/arch/arm/mach-omap1/board-h3.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-h3.c + * + * This file contains OMAP1710 H3 specific code. + * + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2002 MontaVista Software, Inc. + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com + */ +#include <linux/gpio.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/major.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/platnand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/input.h> +#include <linux/spi/spi.h> +#include <linux/mfd/tps65010.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/omap-dma.h> +#include <linux/leds.h> + +#include <asm/setup.h> +#include <asm/page.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "mux.h" +#include "flash.h" +#include "hardware.h" +#include "irqs.h" +#include "usb.h" +#include "common.h" +#include "board-h3.h" + +/* In OMAP1710 H3 the Ethernet is directly connected to CS1 */ +#define OMAP1710_ETHR_START 0x04000300 + +#define H3_TS_GPIO 48 + +static const unsigned int h3_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(1, 0, KEY_RIGHT), + KEY(2, 0, KEY_3), + KEY(3, 0, KEY_F10), + KEY(4, 0, KEY_F5), + KEY(5, 0, KEY_9), + KEY(0, 1, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(2, 1, KEY_2), + KEY(3, 1, KEY_F9), + KEY(4, 1, KEY_F7), + KEY(5, 1, KEY_0), + KEY(0, 2, KEY_ENTER), + KEY(1, 2, KEY_6), + KEY(2, 2, KEY_1), + KEY(3, 2, KEY_F2), + KEY(4, 2, KEY_F6), + KEY(5, 2, KEY_HOME), + KEY(0, 3, KEY_8), + KEY(1, 3, KEY_5), + KEY(2, 3, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(4, 3, KEY_F8), + KEY(5, 3, KEY_END), + KEY(0, 4, KEY_7), + KEY(1, 4, KEY_4), + KEY(2, 4, KEY_F11), + KEY(3, 4, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(5, 4, KEY_ESC), + KEY(0, 5, KEY_F13), + KEY(1, 5, KEY_F14), + KEY(2, 5, KEY_F15), + KEY(3, 5, KEY_F16), + KEY(4, 5, KEY_SLEEP), +}; + + +static struct mtd_partition nor_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data nor_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), +}; + +static struct resource nor_resource = { + /* This is on CS3, wherever it's mapped */ + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nor_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &nor_data, + }, + .num_resources = 1, + .resource = &nor_resource, +}; + +static struct mtd_partition nand_partitions[] = { +#if 0 + /* REVISIT: enable these partitions if you make NAND BOOT work */ + { + .name = "xloader", + .offset = 0, + .size = 64 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "bootloader", + .offset = MTDPART_OFS_APPEND, + .size = 256 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 192 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 2 * SZ_1M, + }, +#endif + { + .name = "filesystem", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + }, +}; + +#define H3_NAND_RB_GPIO_PIN 10 + +static int nand_dev_ready(struct nand_chip *chip) +{ + return gpio_get_value(H3_NAND_RB_GPIO_PIN); +} + +static struct platform_nand_data nand_platdata = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .nr_partitions = ARRAY_SIZE(nand_partitions), + .partitions = nand_partitions, + .options = NAND_SAMSUNG_LP_OPTIONS, + }, + .ctrl = { + .cmd_ctrl = omap1_nand_cmd_ctl, + .dev_ready = nand_dev_ready, + + }, +}; + +static struct resource nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "gen_nand", + .id = 0, + .dev = { + .platform_data = &nand_platdata, + }, + .num_resources = 1, + .resource = &nand_resource, +}; + +static struct smc91x_platdata smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = OMAP1710_ETHR_START, /* Physical */ + .end = OMAP1710_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &smc91x_info, + }, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static void __init h3_init_smc91x(void) +{ + omap_cfg_reg(W15_1710_GPIO40); + if (gpio_request(40, "SMC91x irq") < 0) { + printk("Error requesting gpio 40 for smc91x irq\n"); + return; + } +} + +#define GPTIMER_BASE 0xFFFB1400 +#define GPTIMER_REGS(x) (0xFFFB1400 + (x * 0x800)) +#define GPTIMER_REGS_SIZE 0x46 + +static struct resource intlat_resources[] = { + [0] = { + .start = GPTIMER_REGS(0), /* Physical */ + .end = GPTIMER_REGS(0) + GPTIMER_REGS_SIZE, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_1610_GPTIMER1, + .end = INT_1610_GPTIMER1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device intlat_device = { + .name = "omap_intlat", + .id = 0, + .num_resources = ARRAY_SIZE(intlat_resources), + .resource = intlat_resources, +}; + +static struct resource h3_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data h3_keymap_data = { + .keymap = h3_keymap, + .keymap_size = ARRAY_SIZE(h3_keymap), +}; + +static struct omap_kp_platform_data h3_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &h3_keymap_data, + .rep = true, + .delay = 9, + .dbounce = true, +}; + +static struct platform_device h3_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h3_kp_data, + }, + .num_resources = ARRAY_SIZE(h3_kp_resources), + .resource = h3_kp_resources, +}; + +static struct platform_device h3_lcd_device = { + .name = "lcd_h3", + .id = -1, +}; + +static struct spi_board_info h3_spi_board_info[] __initdata = { + [0] = { + .modalias = "tsc2101", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 16000000, + /* .platform_data = &tsc_platform_data, */ + }, +}; + +static const struct gpio_led h3_gpio_led_pins[] = { + { + .name = "h3:red", + .default_trigger = "heartbeat", + .gpio = 3, + }, + { + .name = "h3:green", + .default_trigger = "cpu0", + .gpio = OMAP_MPUIO(4), + }, +}; + +static struct gpio_led_platform_data h3_gpio_led_data = { + .leds = h3_gpio_led_pins, + .num_leds = ARRAY_SIZE(h3_gpio_led_pins), +}; + +static struct platform_device h3_gpio_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &h3_gpio_led_data, + }, +}; + +static struct platform_device *devices[] __initdata = { + &nor_device, + &nand_device, + &smc91x_device, + &intlat_device, + &h3_kp_device, + &h3_lcd_device, + &h3_gpio_leds, +}; + +static struct omap_usb_config h3_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#if IS_ENABLED(CONFIG_USB_OMAP) + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ +#elif IS_ENABLED(CONFIG_USB_OHCI_HCD) + /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ +#endif + + .pins[1] = 3, +}; + +static const struct omap_lcd_config h3_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static struct i2c_board_info __initdata h3_i2c_board_info[] = { + { + I2C_BOARD_INFO("tps65013", 0x48), + }, + { + I2C_BOARD_INFO("isp1301_omap", 0x2d), + }, +}; + +static void __init h3_init(void) +{ + h3_init_smc91x(); + + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * H3 support NAND-boot, with a dip switch to put NOR on CS2B and NAND + * (which on H2 may be 16bit) on CS3. Try detecting that in code here, + * to avoid probing every possible flash configuration... + */ + nor_resource.end = nor_resource.start = omap_cs3_phys(); + nor_resource.end += SZ_32M - 1; + + nand_resource.end = nand_resource.start = OMAP_CS2B_PHYS; + nand_resource.end += SZ_4K - 1; + BUG_ON(gpio_request(H3_NAND_RB_GPIO_PIN, "NAND ready") < 0); + gpio_direction_input(H3_NAND_RB_GPIO_PIN); + + /* GPIO10 Func_MUX_CTRL reg bit 29:27, Configure V2 to mode1 as GPIO */ + /* GPIO10 pullup/down register, Enable pullup on GPIO10 */ + omap_cfg_reg(V2_1710_GPIO10); + + /* Mux pins for keypad */ + omap_cfg_reg(F18_1610_KBC0); + omap_cfg_reg(D20_1610_KBC1); + omap_cfg_reg(D19_1610_KBC2); + omap_cfg_reg(E18_1610_KBC3); + omap_cfg_reg(C21_1610_KBC4); + omap_cfg_reg(G18_1610_KBR0); + omap_cfg_reg(F19_1610_KBR1); + omap_cfg_reg(H14_1610_KBR2); + omap_cfg_reg(E20_1610_KBR3); + omap_cfg_reg(E19_1610_KBR4); + omap_cfg_reg(N19_1610_KBR5); + + /* GPIO based LEDs */ + omap_cfg_reg(P18_1610_GPIO3); + omap_cfg_reg(MPUIO4); + + smc91x_resources[1].start = gpio_to_irq(40); + smc91x_resources[1].end = gpio_to_irq(40); + platform_add_devices(devices, ARRAY_SIZE(devices)); + h3_spi_board_info[0].irq = gpio_to_irq(H3_TS_GPIO); + spi_register_board_info(h3_spi_board_info, + ARRAY_SIZE(h3_spi_board_info)); + omap_serial_init(); + h3_i2c_board_info[1].irq = gpio_to_irq(14); + omap_register_i2c_bus(1, 100, h3_i2c_board_info, + ARRAY_SIZE(h3_i2c_board_info)); + omap1_usb_init(&h3_usb_config); + h3_mmc_init(); + + omapfb_set_lcd_config(&h3_lcd_config); +} + +MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") + /* Maintainer: Texas Instruments, Inc. */ + .atag_offset = 0x100, + .map_io = omap16xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = h3_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-h3.h b/arch/arm/mach-omap1/board-h3.h new file mode 100644 index 000000000..78de535be --- /dev/null +++ b/arch/arm/mach-omap1/board-h3.h @@ -0,0 +1,35 @@ +/* + * arch/arm/mach-omap1/board-h3.h + * + * Copyright (C) 2001 RidgeRun, Inc. + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __ASM_ARCH_OMAP_H3_H +#define __ASM_ARCH_OMAP_H3_H + +#define H3_TPS_GPIO_BASE (OMAP_MAX_GPIO_LINES + 16 /* MPUIO */) +# define H3_TPS_GPIO_MMC_PWR_EN (H3_TPS_GPIO_BASE + 4) + +extern void h3_mmc_init(void); + +#endif /* __ASM_ARCH_OMAP_H3_H */ diff --git a/arch/arm/mach-omap1/board-htcherald.c b/arch/arm/mach-omap1/board-htcherald.c new file mode 100644 index 000000000..291d294b5 --- /dev/null +++ b/arch/arm/mach-omap1/board-htcherald.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HTC Herald board configuration + * Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com> + * Copyright (C) 2009 Wing Linux + * + * Based on the board-htcwizard.c file from the linwizard project: + * Copyright (C) 2006 Unai Uribarri + * Copyright (C) 2008 linwizard.sourceforge.net + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/i2c.h> +#include <linux/platform_data/i2c-gpio.h> +#include <linux/htcpld.h> +#include <linux/leds.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> +#include <linux/omapfb.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include "hardware.h" +#include "omap7xx.h" +#include "mmc.h" +#include "irqs.h" +#include "usb.h" +#include "common.h" + +/* LCD register definition */ +#define OMAP_LCDC_CONTROL (0xfffec000 + 0x00) +#define OMAP_LCDC_STATUS (0xfffec000 + 0x10) +#define OMAP_DMA_LCD_CCR (0xfffee300 + 0xc2) +#define OMAP_DMA_LCD_CTRL (0xfffee300 + 0xc4) +#define OMAP_LCDC_CTRL_LCD_EN (1 << 0) +#define OMAP_LCDC_STAT_DONE (1 << 0) + +/* GPIO definitions for the power button and keyboard slide switch */ +#define HTCHERALD_GPIO_POWER 139 +#define HTCHERALD_GPIO_SLIDE 174 +#define HTCHERALD_GIRQ_BTNS 141 + +/* GPIO definitions for the touchscreen */ +#define HTCHERALD_GPIO_TS 76 + +/* HTCPLD definitions */ + +/* + * CPLD Logic + * + * Chip 3 - 0x03 + * + * Function 7 6 5 4 3 2 1 0 + * ------------------------------------ + * DPAD light x x x x x x x 1 + * SoundDev x x x x 1 x x x + * Screen white 1 x x x x x x x + * MMC power on x x x x x 1 x x + * Happy times (n) 0 x x x x 1 x x + * + * Chip 4 - 0x04 + * + * Function 7 6 5 4 3 2 1 0 + * ------------------------------------ + * Keyboard light x x x x x x x 1 + * LCD Bright (4) x x x x x 1 1 x + * LCD Bright (3) x x x x x 0 1 x + * LCD Bright (2) x x x x x 1 0 x + * LCD Bright (1) x x x x x 0 0 x + * LCD Off x x x x 0 x x x + * LCD image (fb) 1 x x x x x x x + * LCD image (white) 0 x x x x x x x + * Caps lock LED x x 1 x x x x x + * + * Chip 5 - 0x05 + * + * Function 7 6 5 4 3 2 1 0 + * ------------------------------------ + * Red (solid) x x x x x 1 x x + * Red (flash) x x x x x x 1 x + * Green (GSM flash) x x x x 1 x x x + * Green (GSM solid) x x x 1 x x x x + * Green (wifi flash) x x 1 x x x x x + * Blue (bt flash) x 1 x x x x x x + * DPAD Int Enable 1 x x x x x x 0 + * + * (Combinations of the above can be made for different colors.) + * The direction pad interrupt enable must be set each time the + * interrupt is handled. + * + * Chip 6 - 0x06 + * + * Function 7 6 5 4 3 2 1 0 + * ------------------------------------ + * Vibrator x x x x 1 x x x + * Alt LED x x x 1 x x x x + * Screen white 1 x x x x x x x + * Screen white x x 1 x x x x x + * Screen white x 0 x x x x x x + * Enable kbd dpad x x x x x x 0 x + * Happy Times 0 1 0 x x x 0 x + */ + +/* + * HTCPLD GPIO lines start 16 after OMAP_MAX_GPIO_LINES to account + * for the 16 MPUIO lines. + */ +#define HTCPLD_GPIO_START_OFFSET (OMAP_MAX_GPIO_LINES + 16) +#define HTCPLD_IRQ(chip, offset) (OMAP_IRQ_END + 8 * (chip) + (offset)) +#define HTCPLD_BASE(chip, offset) \ + (HTCPLD_GPIO_START_OFFSET + 8 * (chip) + (offset)) + +#define HTCPLD_GPIO_LED_DPAD HTCPLD_BASE(0, 0) +#define HTCPLD_GPIO_LED_KBD HTCPLD_BASE(1, 0) +#define HTCPLD_GPIO_LED_CAPS HTCPLD_BASE(1, 5) +#define HTCPLD_GPIO_LED_RED_FLASH HTCPLD_BASE(2, 1) +#define HTCPLD_GPIO_LED_RED_SOLID HTCPLD_BASE(2, 2) +#define HTCPLD_GPIO_LED_GREEN_FLASH HTCPLD_BASE(2, 3) +#define HTCPLD_GPIO_LED_GREEN_SOLID HTCPLD_BASE(2, 4) +#define HTCPLD_GPIO_LED_WIFI HTCPLD_BASE(2, 5) +#define HTCPLD_GPIO_LED_BT HTCPLD_BASE(2, 6) +#define HTCPLD_GPIO_LED_VIBRATE HTCPLD_BASE(3, 3) +#define HTCPLD_GPIO_LED_ALT HTCPLD_BASE(3, 4) + +#define HTCPLD_GPIO_RIGHT_KBD HTCPLD_BASE(6, 7) +#define HTCPLD_GPIO_UP_KBD HTCPLD_BASE(6, 6) +#define HTCPLD_GPIO_LEFT_KBD HTCPLD_BASE(6, 5) +#define HTCPLD_GPIO_DOWN_KBD HTCPLD_BASE(6, 4) + +#define HTCPLD_GPIO_RIGHT_DPAD HTCPLD_BASE(7, 7) +#define HTCPLD_GPIO_UP_DPAD HTCPLD_BASE(7, 6) +#define HTCPLD_GPIO_LEFT_DPAD HTCPLD_BASE(7, 5) +#define HTCPLD_GPIO_DOWN_DPAD HTCPLD_BASE(7, 4) +#define HTCPLD_GPIO_ENTER_DPAD HTCPLD_BASE(7, 3) + +/* Chip 5 */ +#define HTCPLD_IRQ_RIGHT_KBD HTCPLD_IRQ(0, 7) +#define HTCPLD_IRQ_UP_KBD HTCPLD_IRQ(0, 6) +#define HTCPLD_IRQ_LEFT_KBD HTCPLD_IRQ(0, 5) +#define HTCPLD_IRQ_DOWN_KBD HTCPLD_IRQ(0, 4) + +/* Chip 6 */ +#define HTCPLD_IRQ_RIGHT_DPAD HTCPLD_IRQ(1, 7) +#define HTCPLD_IRQ_UP_DPAD HTCPLD_IRQ(1, 6) +#define HTCPLD_IRQ_LEFT_DPAD HTCPLD_IRQ(1, 5) +#define HTCPLD_IRQ_DOWN_DPAD HTCPLD_IRQ(1, 4) +#define HTCPLD_IRQ_ENTER_DPAD HTCPLD_IRQ(1, 3) + +/* Keyboard definition */ + +static const unsigned int htc_herald_keymap[] = { + KEY(0, 0, KEY_RECORD), /* Mail button */ + KEY(1, 0, KEY_CAMERA), /* Camera */ + KEY(2, 0, KEY_PHONE), /* Send key */ + KEY(3, 0, KEY_VOLUMEUP), /* Volume up */ + KEY(4, 0, KEY_F2), /* Right bar (landscape) */ + KEY(5, 0, KEY_MAIL), /* Win key (portrait) */ + KEY(6, 0, KEY_DIRECTORY), /* Right bar (portrait) */ + KEY(0, 1, KEY_LEFTCTRL), /* Windows key */ + KEY(1, 1, KEY_COMMA), + KEY(2, 1, KEY_M), + KEY(3, 1, KEY_K), + KEY(4, 1, KEY_SLASH), /* OK key */ + KEY(5, 1, KEY_I), + KEY(6, 1, KEY_U), + KEY(0, 2, KEY_LEFTALT), + KEY(1, 2, KEY_TAB), + KEY(2, 2, KEY_N), + KEY(3, 2, KEY_J), + KEY(4, 2, KEY_ENTER), + KEY(5, 2, KEY_H), + KEY(6, 2, KEY_Y), + KEY(0, 3, KEY_SPACE), + KEY(1, 3, KEY_L), + KEY(2, 3, KEY_B), + KEY(3, 3, KEY_V), + KEY(4, 3, KEY_BACKSPACE), + KEY(5, 3, KEY_G), + KEY(6, 3, KEY_T), + KEY(0, 4, KEY_CAPSLOCK), /* Shift */ + KEY(1, 4, KEY_C), + KEY(2, 4, KEY_F), + KEY(3, 4, KEY_R), + KEY(4, 4, KEY_O), + KEY(5, 4, KEY_E), + KEY(6, 4, KEY_D), + KEY(0, 5, KEY_X), + KEY(1, 5, KEY_Z), + KEY(2, 5, KEY_S), + KEY(3, 5, KEY_W), + KEY(4, 5, KEY_P), + KEY(5, 5, KEY_Q), + KEY(6, 5, KEY_A), + KEY(0, 6, KEY_CONNECT), /* Voice button */ + KEY(2, 6, KEY_CANCEL), /* End key */ + KEY(3, 6, KEY_VOLUMEDOWN), /* Volume down */ + KEY(4, 6, KEY_F1), /* Left bar (landscape) */ + KEY(5, 6, KEY_WWW), /* OK button (portrait) */ + KEY(6, 6, KEY_CALENDAR), /* Left bar (portrait) */ +}; + +static const struct matrix_keymap_data htc_herald_keymap_data = { + .keymap = htc_herald_keymap, + .keymap_size = ARRAY_SIZE(htc_herald_keymap), +}; + +static struct omap_kp_platform_data htcherald_kp_data = { + .rows = 7, + .cols = 7, + .delay = 20, + .rep = true, + .keymap_data = &htc_herald_keymap_data, +}; + +static struct resource kp_resources[] = { + [0] = { + .start = INT_7XX_MPUIO_KEYPAD, + .end = INT_7XX_MPUIO_KEYPAD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &htcherald_kp_data, + }, + .num_resources = ARRAY_SIZE(kp_resources), + .resource = kp_resources, +}; + +/* GPIO buttons for keyboard slide and power button */ +static struct gpio_keys_button herald_gpio_keys_table[] = { + {BTN_0, HTCHERALD_GPIO_POWER, 1, "POWER", EV_KEY, 1, 20}, + {SW_LID, HTCHERALD_GPIO_SLIDE, 0, "SLIDE", EV_SW, 1, 20}, + + {KEY_LEFT, HTCPLD_GPIO_LEFT_KBD, 1, "LEFT", EV_KEY, 1, 20}, + {KEY_RIGHT, HTCPLD_GPIO_RIGHT_KBD, 1, "RIGHT", EV_KEY, 1, 20}, + {KEY_UP, HTCPLD_GPIO_UP_KBD, 1, "UP", EV_KEY, 1, 20}, + {KEY_DOWN, HTCPLD_GPIO_DOWN_KBD, 1, "DOWN", EV_KEY, 1, 20}, + + {KEY_LEFT, HTCPLD_GPIO_LEFT_DPAD, 1, "DLEFT", EV_KEY, 1, 20}, + {KEY_RIGHT, HTCPLD_GPIO_RIGHT_DPAD, 1, "DRIGHT", EV_KEY, 1, 20}, + {KEY_UP, HTCPLD_GPIO_UP_DPAD, 1, "DUP", EV_KEY, 1, 20}, + {KEY_DOWN, HTCPLD_GPIO_DOWN_DPAD, 1, "DDOWN", EV_KEY, 1, 20}, + {KEY_ENTER, HTCPLD_GPIO_ENTER_DPAD, 1, "DENTER", EV_KEY, 1, 20}, +}; + +static struct gpio_keys_platform_data herald_gpio_keys_data = { + .buttons = herald_gpio_keys_table, + .nbuttons = ARRAY_SIZE(herald_gpio_keys_table), + .rep = true, +}; + +static struct platform_device herald_gpiokeys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &herald_gpio_keys_data, + }, +}; + +/* LEDs for the Herald. These connect to the HTCPLD GPIO device. */ +static const struct gpio_led gpio_leds[] = { + {"dpad", NULL, HTCPLD_GPIO_LED_DPAD, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"kbd", NULL, HTCPLD_GPIO_LED_KBD, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"vibrate", NULL, HTCPLD_GPIO_LED_VIBRATE, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"green_solid", NULL, HTCPLD_GPIO_LED_GREEN_SOLID, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"green_flash", NULL, HTCPLD_GPIO_LED_GREEN_FLASH, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"red_solid", "mmc0", HTCPLD_GPIO_LED_RED_SOLID, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"red_flash", NULL, HTCPLD_GPIO_LED_RED_FLASH, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"wifi", NULL, HTCPLD_GPIO_LED_WIFI, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"bt", NULL, HTCPLD_GPIO_LED_BT, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"caps", NULL, HTCPLD_GPIO_LED_CAPS, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, + {"alt", NULL, HTCPLD_GPIO_LED_ALT, 0, 0, LEDS_GPIO_DEFSTATE_OFF}, +}; + +static struct gpio_led_platform_data gpio_leds_data = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device gpio_leds_device = { + .name = "leds-gpio", + .id = 0, + .dev = { + .platform_data = &gpio_leds_data, + }, +}; + +/* HTC PLD chips */ + +static struct resource htcpld_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct htcpld_chip_platform_data htcpld_chips[] = { + [0] = { + .addr = 0x03, + .reset = 0x04, + .num_gpios = 8, + .gpio_out_base = HTCPLD_BASE(0, 0), + .gpio_in_base = HTCPLD_BASE(4, 0), + }, + [1] = { + .addr = 0x04, + .reset = 0x8e, + .num_gpios = 8, + .gpio_out_base = HTCPLD_BASE(1, 0), + .gpio_in_base = HTCPLD_BASE(5, 0), + }, + [2] = { + .addr = 0x05, + .reset = 0x80, + .num_gpios = 8, + .gpio_out_base = HTCPLD_BASE(2, 0), + .gpio_in_base = HTCPLD_BASE(6, 0), + .irq_base = HTCPLD_IRQ(0, 0), + .num_irqs = 8, + }, + [3] = { + .addr = 0x06, + .reset = 0x40, + .num_gpios = 8, + .gpio_out_base = HTCPLD_BASE(3, 0), + .gpio_in_base = HTCPLD_BASE(7, 0), + .irq_base = HTCPLD_IRQ(1, 0), + .num_irqs = 8, + }, +}; + +static struct htcpld_core_platform_data htcpld_pfdata = { + .i2c_adapter_id = 1, + + .chip = htcpld_chips, + .num_chip = ARRAY_SIZE(htcpld_chips), +}; + +static struct platform_device htcpld_device = { + .name = "i2c-htcpld", + .id = -1, + .resource = htcpld_resources, + .num_resources = ARRAY_SIZE(htcpld_resources), + .dev = { + .platform_data = &htcpld_pfdata, + }, +}; + +/* USB Device */ +static struct omap_usb_config htcherald_usb_config __initdata = { + .otg = 0, + .register_host = 0, + .register_dev = 1, + .hmc_mode = 4, + .pins[0] = 2, +}; + +/* LCD Device resources */ +static const struct omap_lcd_config htcherald_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static struct platform_device lcd_device = { + .name = "lcd_htcherald", + .id = -1, +}; + +/* MMC Card */ +#if IS_ENABLED(CONFIG_MMC_OMAP) +static struct omap_mmc_platform_data htc_mmc1_data = { + .nr_slots = 1, + .switch_slot = NULL, + .slots[0] = { + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + .nomux = 1, + .wires = 4, + .switch_pin = -1, + }, +}; + +static struct omap_mmc_platform_data *htc_mmc_data[1]; +#endif + + +/* Platform devices for the Herald */ +static struct platform_device *devices[] __initdata = { + &kp_device, + &lcd_device, + &htcpld_device, + &gpio_leds_device, + &herald_gpiokeys_device, +}; + +/* + * Touchscreen + */ +static const struct ads7846_platform_data htcherald_ts_platform_data = { + .model = 7846, + .keep_vref_on = 1, + .x_plate_ohms = 496, + .gpio_pendown = HTCHERALD_GPIO_TS, + .pressure_max = 10000, + .pressure_min = 5000, + .x_min = 528, + .x_max = 3760, + .y_min = 624, + .y_max = 3760, +}; + +static struct spi_board_info __initdata htcherald_spi_board_info[] = { + { + .modalias = "ads7846", + .platform_data = &htcherald_ts_platform_data, + .max_speed_hz = 2500000, + .bus_num = 2, + .chip_select = 1, + } +}; + +/* + * Init functions from here on + */ + +static void __init htcherald_lcd_init(void) +{ + u32 reg; + unsigned int tries = 200; + + /* disable controller if active */ + reg = omap_readl(OMAP_LCDC_CONTROL); + if (reg & OMAP_LCDC_CTRL_LCD_EN) { + reg &= ~OMAP_LCDC_CTRL_LCD_EN; + omap_writel(reg, OMAP_LCDC_CONTROL); + + /* wait for end of frame */ + while (!(omap_readl(OMAP_LCDC_STATUS) & OMAP_LCDC_STAT_DONE)) { + tries--; + if (!tries) + break; + } + if (!tries) + pr_err("Timeout waiting for end of frame -- LCD may not be available\n"); + + /* turn off DMA */ + reg = omap_readw(OMAP_DMA_LCD_CCR); + reg &= ~(1 << 7); + omap_writew(reg, OMAP_DMA_LCD_CCR); + + reg = omap_readw(OMAP_DMA_LCD_CTRL); + reg &= ~(1 << 8); + omap_writew(reg, OMAP_DMA_LCD_CTRL); + } +} + +static void __init htcherald_map_io(void) +{ + omap7xx_map_io(); + + /* + * The LCD panel must be disabled and DMA turned off here, as doing + * it later causes the LCD never to reinitialize. + */ + htcherald_lcd_init(); + + printk(KERN_INFO "htcherald_map_io done.\n"); +} + +static void __init htcherald_disable_watchdog(void) +{ + /* Disable watchdog if running */ + if (omap_readl(OMAP_WDT_TIMER_MODE) & 0x8000) { + /* + * disable a potentially running watchdog timer before + * it kills us. + */ + printk(KERN_WARNING "OMAP850 Watchdog seems to be activated, disabling it for now.\n"); + omap_writel(0xF5, OMAP_WDT_TIMER_MODE); + omap_writel(0xA0, OMAP_WDT_TIMER_MODE); + } +} + +#define HTCHERALD_GPIO_USB_EN1 33 +#define HTCHERALD_GPIO_USB_EN2 73 +#define HTCHERALD_GPIO_USB_DM 35 +#define HTCHERALD_GPIO_USB_DP 36 + +static void __init htcherald_usb_enable(void) +{ + unsigned int tries = 20; + unsigned int value = 0; + + /* Request the GPIOs we need to control here */ + if (gpio_request(HTCHERALD_GPIO_USB_EN1, "herald_usb") < 0) + goto err1; + + if (gpio_request(HTCHERALD_GPIO_USB_EN2, "herald_usb") < 0) + goto err2; + + if (gpio_request(HTCHERALD_GPIO_USB_DM, "herald_usb") < 0) + goto err3; + + if (gpio_request(HTCHERALD_GPIO_USB_DP, "herald_usb") < 0) + goto err4; + + /* force USB_EN GPIO to 0 */ + do { + /* output low */ + gpio_direction_output(HTCHERALD_GPIO_USB_EN1, 0); + } while ((value = gpio_get_value(HTCHERALD_GPIO_USB_EN1)) == 1 && + --tries); + + if (value == 1) + printk(KERN_WARNING "Unable to reset USB, trying to continue\n"); + + gpio_direction_output(HTCHERALD_GPIO_USB_EN2, 0); /* output low */ + gpio_direction_input(HTCHERALD_GPIO_USB_DM); /* input */ + gpio_direction_input(HTCHERALD_GPIO_USB_DP); /* input */ + + goto done; + +err4: + gpio_free(HTCHERALD_GPIO_USB_DM); +err3: + gpio_free(HTCHERALD_GPIO_USB_EN2); +err2: + gpio_free(HTCHERALD_GPIO_USB_EN1); +err1: + printk(KERN_ERR "Unabled to request GPIO for USB\n"); +done: + printk(KERN_INFO "USB setup complete.\n"); +} + +static void __init htcherald_init(void) +{ + printk(KERN_INFO "HTC Herald init.\n"); + + /* Do board initialization before we register all the devices */ + htcpld_resources[0].start = gpio_to_irq(HTCHERALD_GIRQ_BTNS); + htcpld_resources[0].end = gpio_to_irq(HTCHERALD_GIRQ_BTNS); + platform_add_devices(devices, ARRAY_SIZE(devices)); + + htcherald_disable_watchdog(); + + htcherald_usb_enable(); + omap1_usb_init(&htcherald_usb_config); + + htcherald_spi_board_info[0].irq = gpio_to_irq(HTCHERALD_GPIO_TS); + spi_register_board_info(htcherald_spi_board_info, + ARRAY_SIZE(htcherald_spi_board_info)); + + omap_register_i2c_bus(1, 100, NULL, 0); + +#if IS_ENABLED(CONFIG_MMC_OMAP) + htc_mmc_data[0] = &htc_mmc1_data; + omap1_init_mmc(htc_mmc_data, 1); +#endif + + omapfb_set_lcd_config(&htcherald_lcd_config); +} + +MACHINE_START(HERALD, "HTC Herald") + /* Maintainer: Cory Maccarrone <darkstar6262@gmail.com> */ + /* Maintainer: wing-linux.sourceforge.net */ + .atag_offset = 0x100, + .map_io = htcherald_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = htcherald_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c new file mode 100644 index 000000000..6deb4ca07 --- /dev/null +++ b/arch/arm/mach-omap1/board-innovator.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-innovator.c + * + * Board specific inits for OMAP-1510 and OMAP-1610 Innovator + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> + */ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/input.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> +#include <linux/platform_data/keypad-omap.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "mux.h" +#include "flash.h" +#include "hardware.h" +#include "usb.h" +#include "iomap.h" +#include "common.h" +#include "mmc.h" + +/* At OMAP1610 Innovator the Ethernet is directly connected to CS1 */ +#define INNOVATOR1610_ETHR_START 0x04000300 + +static const unsigned int innovator_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(3, 0, KEY_DOWN), + KEY(1, 1, KEY_F2), + KEY(2, 1, KEY_RIGHT), + KEY(0, 2, KEY_F3), + KEY(1, 2, KEY_F4), + KEY(2, 2, KEY_UP), + KEY(2, 3, KEY_ENTER), + KEY(3, 3, KEY_LEFT), +}; + +static struct mtd_partition innovator_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* rest of flash1 is a file system */ + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16M - SZ_2M - 2 * SZ_128K, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data innovator_flash_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = innovator_partitions, + .nr_parts = ARRAY_SIZE(innovator_partitions), +}; + +static struct resource innovator_flash_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device innovator_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &innovator_flash_data, + }, + .num_resources = 1, + .resource = &innovator_flash_resource, +}; + +static struct resource innovator_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data innovator_keymap_data = { + .keymap = innovator_keymap, + .keymap_size = ARRAY_SIZE(innovator_keymap), +}; + +static struct omap_kp_platform_data innovator_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &innovator_keymap_data, + .delay = 4, +}; + +static struct platform_device innovator_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &innovator_kp_data, + }, + .num_resources = ARRAY_SIZE(innovator_kp_resources), + .resource = innovator_kp_resources, +}; + +static struct smc91x_platdata innovator_smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +#ifdef CONFIG_ARCH_OMAP15XX + +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> + + +/* Only FPGA needs to be mapped here. All others are done with ioremap */ +static struct map_desc innovator1510_io_desc[] __initdata = { + { + .virtual = OMAP1510_FPGA_BASE, + .pfn = __phys_to_pfn(OMAP1510_FPGA_START), + .length = OMAP1510_FPGA_SIZE, + .type = MT_DEVICE + } +}; + +static struct resource innovator1510_smc91x_resources[] = { + [0] = { + .start = OMAP1510_FPGA_ETHR_START, /* Physical */ + .end = OMAP1510_FPGA_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP1510_INT_ETHER, + .end = OMAP1510_INT_ETHER, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + }, +}; + +static struct platform_device innovator1510_smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &innovator_smc91x_info, + }, + .num_resources = ARRAY_SIZE(innovator1510_smc91x_resources), + .resource = innovator1510_smc91x_resources, +}; + +static struct platform_device innovator1510_lcd_device = { + .name = "lcd_inn1510", + .id = -1, + .dev = { + .platform_data = (void __force *)OMAP1510_FPGA_LCD_PANEL_CONTROL, + } +}; + +static struct platform_device innovator1510_spi_device = { + .name = "spi_inn1510", + .id = -1, +}; + +static struct platform_device *innovator1510_devices[] __initdata = { + &innovator_flash_device, + &innovator1510_smc91x_device, + &innovator_kp_device, + &innovator1510_lcd_device, + &innovator1510_spi_device, +}; + +static int innovator_get_pendown_state(void) +{ + return !(__raw_readb(OMAP1510_FPGA_TOUCHSCREEN) & (1 << 5)); +} + +static const struct ads7846_platform_data innovator1510_ts_info = { + .model = 7846, + .vref_delay_usecs = 100, /* internal, no capacitor */ + .x_plate_ohms = 419, + .y_plate_ohms = 486, + .get_pendown_state = innovator_get_pendown_state, +}; + +static struct spi_board_info __initdata innovator1510_boardinfo[] = { { + /* FPGA (bus "10") CS0 has an ads7846e */ + .modalias = "ads7846", + .platform_data = &innovator1510_ts_info, + .irq = OMAP1510_INT_FPGA_TS, + .max_speed_hz = 120000 /* max sample rate at 3V */ + * 26 /* command + data + overhead */, + .bus_num = 10, + .chip_select = 0, +} }; + +#endif /* CONFIG_ARCH_OMAP15XX */ + +#ifdef CONFIG_ARCH_OMAP16XX + +static struct resource innovator1610_smc91x_resources[] = { + [0] = { + .start = INNOVATOR1610_ETHR_START, /* Physical */ + .end = INNOVATOR1610_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + }, +}; + +static struct platform_device innovator1610_smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &innovator_smc91x_info, + }, + .num_resources = ARRAY_SIZE(innovator1610_smc91x_resources), + .resource = innovator1610_smc91x_resources, +}; + +static struct platform_device innovator1610_lcd_device = { + .name = "inn1610_lcd", + .id = -1, +}; + +static struct platform_device *innovator1610_devices[] __initdata = { + &innovator_flash_device, + &innovator1610_smc91x_device, + &innovator_kp_device, + &innovator1610_lcd_device, +}; + +#endif /* CONFIG_ARCH_OMAP16XX */ + +static void __init innovator_init_smc91x(void) +{ + if (cpu_is_omap1510()) { + __raw_writeb(__raw_readb(OMAP1510_FPGA_RST) & ~1, + OMAP1510_FPGA_RST); + udelay(750); + } else { + if (gpio_request(0, "SMC91x irq") < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } + } +} + +#ifdef CONFIG_ARCH_OMAP15XX +/* + * Board specific gang-switched transceiver power on/off. + */ +static int innovator_omap_ohci_transceiver_power(int on) +{ + if (on) + __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) + | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + else + __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) + & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + + return 0; +} + +static struct omap_usb_config innovator1510_usb_config __initdata = { + /* for bundled non-standard host and peripheral cables */ + .hmc_mode = 4, + + .register_host = 1, + .pins[1] = 6, + .pins[2] = 6, /* Conflicts with UART2 */ + + .register_dev = 1, + .pins[0] = 2, + + .transceiver_power = innovator_omap_ohci_transceiver_power, +}; + +static const struct omap_lcd_config innovator1510_lcd_config __initconst = { + .ctrl_name = "internal", +}; +#endif + +#ifdef CONFIG_ARCH_OMAP16XX +static struct omap_usb_config h2_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#if IS_ENABLED(CONFIG_USB_OMAP) + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ + /* .hmc_mode = 21,*/ /* 0:host(off) 1:dev(loopback) 2:host(loopback) */ +#elif IS_ENABLED(CONFIG_USB_OHCI_HCD) + /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ +#endif + + .pins[1] = 3, +}; + +static const struct omap_lcd_config innovator1610_lcd_config __initconst = { + .ctrl_name = "internal", +}; +#endif + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static int mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + if (power_on) + __raw_writeb(__raw_readb(OMAP1510_FPGA_POWER) | (1 << 3), + OMAP1510_FPGA_POWER); + else + __raw_writeb(__raw_readb(OMAP1510_FPGA_POWER) & ~(1 << 3), + OMAP1510_FPGA_POWER); + + return 0; +} + +/* + * Innovator could use the following functions tested: + * - mmc_get_wp that uses OMAP_MPUIO(3) + * - mmc_get_cover_state that uses FPGA F4 UIO43 + */ +static struct omap_mmc_platform_data mmc1_data = { + .nr_slots = 1, + .slots[0] = { + .set_power = mmc_set_power, + .wires = 4, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC]; + +static void __init innovator_mmc_init(void) +{ + mmc_data[0] = &mmc1_data; + omap1_init_mmc(mmc_data, OMAP15XX_NR_MMC); +} + +#else +static inline void innovator_mmc_init(void) +{ +} +#endif + +static void __init innovator_init(void) +{ + if (cpu_is_omap1510()) + omap1510_fpga_init_irq(); + innovator_init_smc91x(); + +#ifdef CONFIG_ARCH_OMAP15XX + if (cpu_is_omap1510()) { + unsigned char reg; + + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + reg = __raw_readb(OMAP1510_FPGA_POWER); + reg |= OMAP1510_FPGA_PCR_COM1_EN; + __raw_writeb(reg, OMAP1510_FPGA_POWER); + udelay(10); + + reg = __raw_readb(OMAP1510_FPGA_POWER); + reg |= OMAP1510_FPGA_PCR_COM2_EN; + __raw_writeb(reg, OMAP1510_FPGA_POWER); + udelay(10); + + platform_add_devices(innovator1510_devices, ARRAY_SIZE(innovator1510_devices)); + spi_register_board_info(innovator1510_boardinfo, + ARRAY_SIZE(innovator1510_boardinfo)); + } +#endif +#ifdef CONFIG_ARCH_OMAP16XX + if (!cpu_is_omap1510()) { + innovator1610_smc91x_resources[1].start = gpio_to_irq(0); + innovator1610_smc91x_resources[1].end = gpio_to_irq(0); + platform_add_devices(innovator1610_devices, ARRAY_SIZE(innovator1610_devices)); + } +#endif + +#ifdef CONFIG_ARCH_OMAP15XX + if (cpu_is_omap1510()) { + omap1_usb_init(&innovator1510_usb_config); + omapfb_set_lcd_config(&innovator1510_lcd_config); + } +#endif +#ifdef CONFIG_ARCH_OMAP16XX + if (cpu_is_omap1610()) { + omap1_usb_init(&h2_usb_config); + omapfb_set_lcd_config(&innovator1610_lcd_config); + } +#endif + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + innovator_mmc_init(); +} + +/* + * REVISIT: Assume 15xx for now, we don't want to do revision check + * until later on. The right way to fix this is to set up a different + * machine_id for 16xx Innovator, or use device tree. + */ +static void __init innovator_map_io(void) +{ +#ifdef CONFIG_ARCH_OMAP15XX + omap15xx_map_io(); + + iotable_init(innovator1510_io_desc, ARRAY_SIZE(innovator1510_io_desc)); + udelay(10); /* Delay needed for FPGA */ + + /* Dump the Innovator FPGA rev early - useful info for support. */ + pr_debug("Innovator FPGA Rev %d.%d Board Rev %d\n", + __raw_readb(OMAP1510_FPGA_REV_HIGH), + __raw_readb(OMAP1510_FPGA_REV_LOW), + __raw_readb(OMAP1510_FPGA_BOARD_REV)); +#endif +} + +MACHINE_START(OMAP_INNOVATOR, "TI-Innovator") + /* Maintainer: MontaVista Software, Inc. */ + .atag_offset = 0x100, + .map_io = innovator_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = innovator_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-nand.c b/arch/arm/mach-omap1/board-nand.c new file mode 100644 index 000000000..479ab9be7 --- /dev/null +++ b/arch/arm/mach-omap1/board-nand.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-nand.c + * + * Common OMAP1 board NAND code + * + * Copyright (C) 2004, 2012 Texas Instruments, Inc. + * Copyright (C) 2002 MontaVista Software, Inc. + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> + +#include "common.h" + +void omap1_nand_cmd_ctl(struct nand_chip *this, int cmd, unsigned int ctrl) +{ + unsigned long mask; + + if (cmd == NAND_CMD_NONE) + return; + + mask = (ctrl & NAND_CLE) ? 0x02 : 0; + if (ctrl & NAND_ALE) + mask |= 0x04; + + writeb(cmd, this->legacy.IO_ADDR_W + mask); +} + diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c new file mode 100644 index 000000000..8e0e58495 --- /dev/null +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-nokia770.c + * + * Modified from board-generic.c + */ +#include <linux/clkdev.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/omapfb.h> + +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> +#include <linux/workqueue.h> +#include <linux/delay.h> + +#include <linux/platform_data/keypad-omap.h> +#include <linux/platform_data/lcd-mipid.h> +#include <linux/platform_data/gpio-omap.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "mux.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" +#include "clock.h" +#include "mmc.h" + +#define ADS7846_PENDOWN_GPIO 15 + +static const unsigned int nokia770_keymap[] = { + KEY(1, 0, GROUP_0 | KEY_UP), + KEY(2, 0, GROUP_1 | KEY_F5), + KEY(0, 1, GROUP_0 | KEY_LEFT), + KEY(1, 1, GROUP_0 | KEY_ENTER), + KEY(2, 1, GROUP_0 | KEY_RIGHT), + KEY(0, 2, GROUP_1 | KEY_ESC), + KEY(1, 2, GROUP_0 | KEY_DOWN), + KEY(2, 2, GROUP_1 | KEY_F4), + KEY(0, 3, GROUP_2 | KEY_F7), + KEY(1, 3, GROUP_2 | KEY_F8), + KEY(2, 3, GROUP_2 | KEY_F6), +}; + +static struct resource nokia770_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data nokia770_keymap_data = { + .keymap = nokia770_keymap, + .keymap_size = ARRAY_SIZE(nokia770_keymap), +}; + +static struct omap_kp_platform_data nokia770_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &nokia770_keymap_data, + .delay = 4, +}; + +static struct platform_device nokia770_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &nokia770_kp_data, + }, + .num_resources = ARRAY_SIZE(nokia770_kp_resources), + .resource = nokia770_kp_resources, +}; + +static struct platform_device *nokia770_devices[] __initdata = { + &nokia770_kp_device, +}; + +static void mipid_shutdown(struct mipid_platform_data *pdata) +{ + if (pdata->nreset_gpio != -1) { + printk(KERN_INFO "shutdown LCD\n"); + gpio_set_value(pdata->nreset_gpio, 0); + msleep(120); + } +} + +static struct mipid_platform_data nokia770_mipid_platform_data = { + .shutdown = mipid_shutdown, +}; + +static const struct omap_lcd_config nokia770_lcd_config __initconst = { + .ctrl_name = "hwa742", +}; + +static void __init mipid_dev_init(void) +{ + nokia770_mipid_platform_data.nreset_gpio = 13; + nokia770_mipid_platform_data.data_lines = 16; + + omapfb_set_lcd_config(&nokia770_lcd_config); +} + +static struct ads7846_platform_data nokia770_ads7846_platform_data __initdata = { + .x_max = 0x0fff, + .y_max = 0x0fff, + .x_plate_ohms = 180, + .pressure_max = 255, + .debounce_max = 10, + .debounce_tol = 3, + .debounce_rep = 1, + .gpio_pendown = ADS7846_PENDOWN_GPIO, +}; + +static struct spi_board_info nokia770_spi_board_info[] __initdata = { + [0] = { + .modalias = "lcd_mipid", + .bus_num = 2, + .chip_select = 3, + .max_speed_hz = 12000000, + .platform_data = &nokia770_mipid_platform_data, + }, + [1] = { + .modalias = "ads7846", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 2500000, + .platform_data = &nokia770_ads7846_platform_data, + }, +}; + +static void __init hwa742_dev_init(void) +{ + clk_add_alias("hwa_sys_ck", NULL, "bclk", NULL); +} + +/* assume no Mini-AB port */ + +static struct omap_usb_config nokia770_usb_config __initdata = { + .otg = 1, + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 6, + .extcon = "tahvo-usb", +}; + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +#define NOKIA770_GPIO_MMC_POWER 41 +#define NOKIA770_GPIO_MMC_SWITCH 23 + +static int nokia770_mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + gpio_set_value(NOKIA770_GPIO_MMC_POWER, power_on); + return 0; +} + +static int nokia770_mmc_get_cover_state(struct device *dev, int slot) +{ + return gpio_get_value(NOKIA770_GPIO_MMC_SWITCH); +} + +static struct omap_mmc_platform_data nokia770_mmc2_data = { + .nr_slots = 1, + .max_freq = 12000000, + .slots[0] = { + .set_power = nokia770_mmc_set_power, + .get_cover_state = nokia770_mmc_get_cover_state, + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *nokia770_mmc_data[OMAP16XX_NR_MMC]; + +static void __init nokia770_mmc_init(void) +{ + int ret; + + ret = gpio_request(NOKIA770_GPIO_MMC_POWER, "MMC power"); + if (ret < 0) + return; + gpio_direction_output(NOKIA770_GPIO_MMC_POWER, 0); + + ret = gpio_request(NOKIA770_GPIO_MMC_SWITCH, "MMC cover"); + if (ret < 0) { + gpio_free(NOKIA770_GPIO_MMC_POWER); + return; + } + gpio_direction_input(NOKIA770_GPIO_MMC_SWITCH); + + /* Only the second MMC controller is used */ + nokia770_mmc_data[1] = &nokia770_mmc2_data; + omap1_init_mmc(nokia770_mmc_data, OMAP16XX_NR_MMC); +} + +#else +static inline void nokia770_mmc_init(void) +{ +} +#endif + +#if IS_ENABLED(CONFIG_I2C_CBUS_GPIO) +static struct gpiod_lookup_table nokia770_cbus_gpio_table = { + .dev_id = "i2c-cbus-gpio.2", + .table = { + GPIO_LOOKUP_IDX("mpuio", 9, NULL, 0, 0), /* clk */ + GPIO_LOOKUP_IDX("mpuio", 10, NULL, 1, 0), /* dat */ + GPIO_LOOKUP_IDX("mpuio", 11, NULL, 2, 0), /* sel */ + { }, + }, +}; + +static struct platform_device nokia770_cbus_device = { + .name = "i2c-cbus-gpio", + .id = 2, +}; + +static struct i2c_board_info nokia770_i2c_board_info_2[] __initdata = { + { + I2C_BOARD_INFO("retu", 0x01), + }, + { + I2C_BOARD_INFO("tahvo", 0x02), + }, +}; + +static void __init nokia770_cbus_init(void) +{ + const int retu_irq_gpio = 62; + const int tahvo_irq_gpio = 40; + + if (gpio_request_one(retu_irq_gpio, GPIOF_IN, "Retu IRQ")) + return; + if (gpio_request_one(tahvo_irq_gpio, GPIOF_IN, "Tahvo IRQ")) { + gpio_free(retu_irq_gpio); + return; + } + irq_set_irq_type(gpio_to_irq(retu_irq_gpio), IRQ_TYPE_EDGE_RISING); + irq_set_irq_type(gpio_to_irq(tahvo_irq_gpio), IRQ_TYPE_EDGE_RISING); + nokia770_i2c_board_info_2[0].irq = gpio_to_irq(retu_irq_gpio); + nokia770_i2c_board_info_2[1].irq = gpio_to_irq(tahvo_irq_gpio); + i2c_register_board_info(2, nokia770_i2c_board_info_2, + ARRAY_SIZE(nokia770_i2c_board_info_2)); + gpiod_add_lookup_table(&nokia770_cbus_gpio_table); + platform_device_register(&nokia770_cbus_device); +} +#else /* CONFIG_I2C_CBUS_GPIO */ +static void __init nokia770_cbus_init(void) +{ +} +#endif /* CONFIG_I2C_CBUS_GPIO */ + +static void __init omap_nokia770_init(void) +{ + /* On Nokia 770, the SleepX signal is masked with an + * MPUIO line by default. It has to be unmasked for it + * to become functional */ + + /* SleepX mask direction */ + omap_writew((omap_readw(0xfffb5008) & ~2), 0xfffb5008); + /* Unmask SleepX signal */ + omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004); + + platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices)); + nokia770_spi_board_info[1].irq = gpio_to_irq(15); + spi_register_board_info(nokia770_spi_board_info, + ARRAY_SIZE(nokia770_spi_board_info)); + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + hwa742_dev_init(); + mipid_dev_init(); + omap1_usb_init(&nokia770_usb_config); + nokia770_mmc_init(); + nokia770_cbus_init(); +} + +MACHINE_START(NOKIA770, "Nokia 770") + .atag_offset = 0x100, + .map_io = omap16xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_nokia770_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c new file mode 100644 index 000000000..76684b7a4 --- /dev/null +++ b/arch/arm/mach-omap1/board-osk.c @@ -0,0 +1,664 @@ +/* + * linux/arch/arm/mach-omap1/board-osk.c + * + * Board specific init for OMAP5912 OSK + * + * Written by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/mfd/tps65010.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/platform_data/omap1_bl.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "flash.h" +#include "mux.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" + +/* Name of the GPIO chip used by the OMAP for GPIOs 0..15 */ +#define OMAP_GPIO_LABEL "gpio-0-15" + +/* At OMAP5912 OSK the Ethernet is directly connected to CS1 */ +#define OMAP_OSK_ETHR_START 0x04800300 + +/* TPS65010 has four GPIOs. nPG and LED2 can be treated like GPIOs with + * alternate pin configurations for hardware-controlled blinking. + */ +#define OSK_TPS_GPIO_BASE (OMAP_MAX_GPIO_LINES + 16 /* MPUIO */) +# define OSK_TPS_GPIO_USB_PWR_EN (OSK_TPS_GPIO_BASE + 0) +# define OSK_TPS_GPIO_LED_D3 (OSK_TPS_GPIO_BASE + 1) +# define OSK_TPS_GPIO_LAN_RESET (OSK_TPS_GPIO_BASE + 2) +# define OSK_TPS_GPIO_DSP_PWR_EN (OSK_TPS_GPIO_BASE + 3) +# define OSK_TPS_GPIO_LED_D9 (OSK_TPS_GPIO_BASE + 4) +# define OSK_TPS_GPIO_LED_D2 (OSK_TPS_GPIO_BASE + 5) + +static struct mtd_partition osk_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data osk_flash_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = osk_partitions, + .nr_parts = ARRAY_SIZE(osk_partitions), +}; + +static struct resource osk_flash_resource = { + /* this is on CS3, wherever it's mapped */ + .flags = IORESOURCE_MEM, +}; + +static struct platform_device osk5912_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &osk_flash_data, + }, + .num_resources = 1, + .resource = &osk_flash_resource, +}; + +static struct smc91x_platdata osk5912_smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +static struct resource osk5912_smc91x_resources[] = { + [0] = { + .start = OMAP_OSK_ETHR_START, /* Physical */ + .end = OMAP_OSK_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + }, +}; + +static struct platform_device osk5912_smc91x_device = { + .name = "smc91x", + .id = -1, + .dev = { + .platform_data = &osk5912_smc91x_info, + }, + .num_resources = ARRAY_SIZE(osk5912_smc91x_resources), + .resource = osk5912_smc91x_resources, +}; + +static struct resource osk5912_cf_resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device osk5912_cf_device = { + .name = "omap_cf", + .id = -1, + .num_resources = ARRAY_SIZE(osk5912_cf_resources), + .resource = osk5912_cf_resources, +}; + +static struct platform_device *osk5912_devices[] __initdata = { + &osk5912_flash_device, + &osk5912_smc91x_device, + &osk5912_cf_device, +}; + +static const struct gpio_led tps_leds[] = { + /* NOTE: D9 and D2 have hardware blink support. + * Also, D9 requires non-battery power. + */ + { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9", + .default_trigger = "disk-activity", }, + { .gpio = OSK_TPS_GPIO_LED_D2, .name = "d2", }, + { .gpio = OSK_TPS_GPIO_LED_D3, .name = "d3", .active_low = 1, + .default_trigger = "heartbeat", }, +}; + +static struct gpio_led_platform_data tps_leds_data = { + .num_leds = 3, + .leds = tps_leds, +}; + +static struct platform_device osk5912_tps_leds = { + .name = "leds-gpio", + .id = 0, + .dev.platform_data = &tps_leds_data, +}; + +static int osk_tps_setup(struct i2c_client *client, void *context) +{ + if (!IS_BUILTIN(CONFIG_TPS65010)) + return -ENOSYS; + + /* Set GPIO 1 HIGH to disable VBUS power supply; + * OHCI driver powers it up/down as needed. + */ + gpio_request(OSK_TPS_GPIO_USB_PWR_EN, "n_vbus_en"); + gpio_direction_output(OSK_TPS_GPIO_USB_PWR_EN, 1); + /* Free the GPIO again as the driver will request it */ + gpio_free(OSK_TPS_GPIO_USB_PWR_EN); + + /* Set GPIO 2 high so LED D3 is off by default */ + tps65010_set_gpio_out_value(GPIO2, HIGH); + + /* Set GPIO 3 low to take ethernet out of reset */ + gpio_request(OSK_TPS_GPIO_LAN_RESET, "smc_reset"); + gpio_direction_output(OSK_TPS_GPIO_LAN_RESET, 0); + + /* GPIO4 is VDD_DSP */ + gpio_request(OSK_TPS_GPIO_DSP_PWR_EN, "dsp_power"); + gpio_direction_output(OSK_TPS_GPIO_DSP_PWR_EN, 1); + /* REVISIT if DSP support isn't configured, power it off ... */ + + /* Let LED1 (D9) blink; leds-gpio may override it */ + tps65010_set_led(LED1, BLINK); + + /* Set LED2 off by default */ + tps65010_set_led(LED2, OFF); + + /* Enable LOW_PWR handshake */ + tps65010_set_low_pwr(ON); + + /* Switch VLDO2 to 3.0V for AIC23 */ + tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V + | TPS_LDO1_ENABLE); + + /* register these three LEDs */ + osk5912_tps_leds.dev.parent = &client->dev; + platform_device_register(&osk5912_tps_leds); + + return 0; +} + +static struct tps65010_board tps_board = { + .base = OSK_TPS_GPIO_BASE, + .outmask = 0x0f, + .setup = osk_tps_setup, +}; + +static struct i2c_board_info __initdata osk_i2c_board_info[] = { + { + /* This device will get the name "i2c-tps65010" */ + I2C_BOARD_INFO("tps65010", 0x48), + .dev_name = "tps65010", + .platform_data = &tps_board, + + }, + { + I2C_BOARD_INFO("tlv320aic23", 0x1B), + }, + /* TODO when driver support is ready: + * - optionally on Mistral, ov9640 camera sensor at 0x30 + */ +}; + +static void __init osk_init_smc91x(void) +{ + u32 l; + + if ((gpio_request(0, "smc_irq")) < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } + + /* Check EMIFS wait states to fix errors with SMC_GET_PKT_HDR */ + l = omap_readl(EMIFS_CCS(1)); + l |= 0x3; + omap_writel(l, EMIFS_CCS(1)); +} + +static void __init osk_init_cf(int seg) +{ + struct resource *res = &osk5912_cf_resources[1]; + + omap_cfg_reg(M7_1610_GPIO62); + if ((gpio_request(62, "cf_irq")) < 0) { + printk("Error requesting gpio 62 for CF irq\n"); + return; + } + + switch (seg) { + /* NOTE: CS0 could be configured too ... */ + case 1: + res->start = OMAP_CS1_PHYS; + break; + case 2: + res->start = OMAP_CS2_PHYS; + break; + case 3: + res->start = omap_cs3_phys(); + break; + } + + res->end = res->start + SZ_8K - 1; + osk5912_cf_device.dev.platform_data = (void *)(uintptr_t)seg; + + /* NOTE: better EMIFS setup might support more cards; but the + * TRM only shows how to affect regular flash signals, not their + * CF/PCMCIA variants... + */ + pr_debug("%s: cs%d, previous ccs %08x acs %08x\n", __func__, + seg, omap_readl(EMIFS_CCS(seg)), omap_readl(EMIFS_ACS(seg))); + omap_writel(0x0004a1b3, EMIFS_CCS(seg)); /* synch mode 4 etc */ + omap_writel(0x00000000, EMIFS_ACS(seg)); /* OE hold/setup */ + + /* the CF I/O IRQ is really active-low */ + irq_set_irq_type(gpio_to_irq(62), IRQ_TYPE_EDGE_FALLING); +} + +static struct gpiod_lookup_table osk_usb_gpio_table = { + .dev_id = "ohci", + .table = { + /* Power GPIO on the I2C-attached TPS65010 */ + GPIO_LOOKUP("tps65010", 0, "power", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(OMAP_GPIO_LABEL, 9, "overcurrent", + GPIO_ACTIVE_HIGH), + }, +}; + +static struct omap_usb_config osk_usb_config __initdata = { + /* has usb host connector (A) ... for development it can also + * be used, with a NONSTANDARD gender-bending cable/dongle, as + * a peripheral. + */ +#if IS_ENABLED(CONFIG_USB_OMAP) + .register_dev = 1, + .hmc_mode = 0, +#else + .register_host = 1, + .hmc_mode = 16, + .rwc = 1, +#endif + .pins[0] = 2, +}; + +#ifdef CONFIG_OMAP_OSK_MISTRAL +static const struct omap_lcd_config osk_lcd_config __initconst = { + .ctrl_name = "internal", +}; +#endif + +#ifdef CONFIG_OMAP_OSK_MISTRAL + +#include <linux/input.h> +#include <linux/property.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> + +#include <linux/platform_data/keypad-omap.h> + +static const struct property_entry mistral_at24_properties[] = { + PROPERTY_ENTRY_U32("pagesize", 16), + { } +}; + +static const struct software_node mistral_at24_node = { + .properties = mistral_at24_properties, +}; + +static struct i2c_board_info __initdata mistral_i2c_board_info[] = { + { + /* NOTE: powered from LCD supply */ + I2C_BOARD_INFO("24c04", 0x50), + .swnode = &mistral_at24_node, + }, + /* TODO when driver support is ready: + * - optionally ov9640 camera sensor at 0x30 + */ +}; + +static const unsigned int osk_keymap[] = { + /* KEY(col, row, code) */ + KEY(0, 0, KEY_F1), /* SW4 */ + KEY(3, 0, KEY_UP), /* (sw2/up) */ + KEY(1, 1, KEY_LEFTCTRL), /* SW5 */ + KEY(2, 1, KEY_LEFT), /* (sw2/left) */ + KEY(0, 2, KEY_SPACE), /* SW3 */ + KEY(1, 2, KEY_ESC), /* SW6 */ + KEY(2, 2, KEY_DOWN), /* (sw2/down) */ + KEY(2, 3, KEY_ENTER), /* (sw2/select) */ + KEY(3, 3, KEY_RIGHT), /* (sw2/right) */ +}; + +static const struct matrix_keymap_data osk_keymap_data = { + .keymap = osk_keymap, + .keymap_size = ARRAY_SIZE(osk_keymap), +}; + +static struct omap_kp_platform_data osk_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &osk_keymap_data, + .delay = 9, +}; + +static struct resource osk5912_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device osk5912_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &osk_kp_data, + }, + .num_resources = ARRAY_SIZE(osk5912_kp_resources), + .resource = osk5912_kp_resources, +}; + +static struct omap_backlight_config mistral_bl_data = { + .default_intensity = 0xa0, +}; + +static struct platform_device mistral_bl_device = { + .name = "omap-bl", + .id = -1, + .dev = { + .platform_data = &mistral_bl_data, + }, +}; + +static struct platform_device osk5912_lcd_device = { + .name = "lcd_osk", + .id = -1, +}; + +static const struct gpio_led mistral_gpio_led_pins[] = { + { + .name = "mistral:red", + .default_trigger = "heartbeat", + .gpio = 3, + }, + { + .name = "mistral:green", + .default_trigger = "cpu0", + .gpio = OMAP_MPUIO(4), + }, +}; + +static struct gpio_led_platform_data mistral_gpio_led_data = { + .leds = mistral_gpio_led_pins, + .num_leds = ARRAY_SIZE(mistral_gpio_led_pins), +}; + +static struct platform_device mistral_gpio_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &mistral_gpio_led_data, + }, +}; + +static struct platform_device *mistral_devices[] __initdata = { + &osk5912_kp_device, + &mistral_bl_device, + &osk5912_lcd_device, + &mistral_gpio_leds, +}; + +static int mistral_get_pendown_state(void) +{ + return !gpio_get_value(4); +} + +static const struct ads7846_platform_data mistral_ts_info = { + .model = 7846, + .vref_delay_usecs = 100, /* internal, no capacitor */ + .x_plate_ohms = 419, + .y_plate_ohms = 486, + .get_pendown_state = mistral_get_pendown_state, +}; + +static struct spi_board_info __initdata mistral_boardinfo[] = { { + /* MicroWire (bus 2) CS0 has an ads7846e */ + .modalias = "ads7846", + .platform_data = &mistral_ts_info, + .max_speed_hz = 120000 /* max sample rate at 3V */ + * 26 /* command + data + overhead */, + .bus_num = 2, + .chip_select = 0, +} }; + +static irqreturn_t +osk_mistral_wake_interrupt(int irq, void *ignored) +{ + return IRQ_HANDLED; +} + +static void __init osk_mistral_init(void) +{ + /* NOTE: we could actually tell if there's a Mistral board + * attached, e.g. by trying to read something from the ads7846. + * But this arch_init() code is too early for that, since we + * can't talk to the ads or even the i2c eeprom. + */ + + /* parallel camera interface */ + omap_cfg_reg(J15_1610_CAM_LCLK); + omap_cfg_reg(J18_1610_CAM_D7); + omap_cfg_reg(J19_1610_CAM_D6); + omap_cfg_reg(J14_1610_CAM_D5); + omap_cfg_reg(K18_1610_CAM_D4); + omap_cfg_reg(K19_1610_CAM_D3); + omap_cfg_reg(K15_1610_CAM_D2); + omap_cfg_reg(K14_1610_CAM_D1); + omap_cfg_reg(L19_1610_CAM_D0); + omap_cfg_reg(L18_1610_CAM_VS); + omap_cfg_reg(L15_1610_CAM_HS); + omap_cfg_reg(M19_1610_CAM_RSTZ); + omap_cfg_reg(Y15_1610_CAM_OUTCLK); + + /* serial camera interface */ + omap_cfg_reg(H19_1610_CAM_EXCLK); + omap_cfg_reg(W13_1610_CCP_CLKM); + omap_cfg_reg(Y12_1610_CCP_CLKP); + /* CCP_DATAM CONFLICTS WITH UART1.TX (and serial console) */ + /* omap_cfg_reg(Y14_1610_CCP_DATAM); */ + omap_cfg_reg(W14_1610_CCP_DATAP); + + /* CAM_PWDN */ + if (gpio_request(11, "cam_pwdn") == 0) { + omap_cfg_reg(N20_1610_GPIO11); + gpio_direction_output(11, 0); + } else + pr_debug("OSK+Mistral: CAM_PWDN is awol\n"); + + + /* omap_cfg_reg(P19_1610_GPIO6); */ /* BUSY */ + gpio_request(6, "ts_busy"); + gpio_direction_input(6); + + omap_cfg_reg(P20_1610_GPIO4); /* PENIRQ */ + gpio_request(4, "ts_int"); + gpio_direction_input(4); + irq_set_irq_type(gpio_to_irq(4), IRQ_TYPE_EDGE_FALLING); + + mistral_boardinfo[0].irq = gpio_to_irq(4); + spi_register_board_info(mistral_boardinfo, + ARRAY_SIZE(mistral_boardinfo)); + + /* the sideways button (SW1) is for use as a "wakeup" button + * + * NOTE: The Mistral board has the wakeup button (SW1) wired + * to the LCD 3.3V rail, which is powered down during suspend. + * To allow this button to wake up the omap, work around this + * HW bug by rewiring SW1 to use the main 3.3V rail. + */ + omap_cfg_reg(N15_1610_MPUIO2); + if (gpio_request(OMAP_MPUIO(2), "wakeup") == 0) { + int ret = 0; + int irq = gpio_to_irq(OMAP_MPUIO(2)); + + gpio_direction_input(OMAP_MPUIO(2)); + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); + /* share the IRQ in case someone wants to use the + * button for more than wakeup from system sleep. + */ + ret = request_irq(irq, + &osk_mistral_wake_interrupt, + IRQF_SHARED, "mistral_wakeup", + &osk_mistral_wake_interrupt); + if (ret != 0) { + gpio_free(OMAP_MPUIO(2)); + printk(KERN_ERR "OSK+Mistral: no wakeup irq, %d?\n", + ret); + } else + enable_irq_wake(irq); + } else + printk(KERN_ERR "OSK+Mistral: wakeup button is awol\n"); + + /* LCD: backlight, and power; power controls other devices on the + * board, like the touchscreen, EEPROM, and wakeup (!) switch. + */ + omap_cfg_reg(PWL); + if (gpio_request(2, "lcd_pwr") == 0) + gpio_direction_output(2, 1); + + /* + * GPIO based LEDs + */ + omap_cfg_reg(P18_1610_GPIO3); + omap_cfg_reg(MPUIO4); + + i2c_register_board_info(1, mistral_i2c_board_info, + ARRAY_SIZE(mistral_i2c_board_info)); + + platform_add_devices(mistral_devices, ARRAY_SIZE(mistral_devices)); +} +#else +static void __init osk_mistral_init(void) { } +#endif + +#define EMIFS_CS3_VAL (0x88013141) + +static void __init osk_init(void) +{ + u32 l; + + osk_init_smc91x(); + osk_init_cf(2); /* CS2 */ + + /* Workaround for wrong CS3 (NOR flash) timing + * There are some U-Boot versions out there which configure + * wrong CS3 memory timings. This mainly leads to CRC + * or similar errors if you use NOR flash (e.g. with JFFS2) + */ + l = omap_readl(EMIFS_CCS(3)); + if (l != EMIFS_CS3_VAL) + omap_writel(EMIFS_CS3_VAL, EMIFS_CCS(3)); + + osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys(); + osk_flash_resource.end += SZ_32M - 1; + osk5912_smc91x_resources[1].start = gpio_to_irq(0); + osk5912_smc91x_resources[1].end = gpio_to_irq(0); + osk5912_cf_resources[0].start = gpio_to_irq(62); + osk5912_cf_resources[0].end = gpio_to_irq(62); + platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l |= (3 << 1); + omap_writel(l, USB_TRANSCEIVER_CTRL); + + gpiod_add_lookup_table(&osk_usb_gpio_table); + omap1_usb_init(&osk_usb_config); + + /* irq for tps65010 chip */ + /* bootloader effectively does: omap_cfg_reg(U19_1610_MPUIO1); */ + if (gpio_request(OMAP_MPUIO(1), "tps65010") == 0) + gpio_direction_input(OMAP_MPUIO(1)); + + omap_serial_init(); + osk_i2c_board_info[0].irq = gpio_to_irq(OMAP_MPUIO(1)); + omap_register_i2c_bus(1, 400, osk_i2c_board_info, + ARRAY_SIZE(osk_i2c_board_info)); + osk_mistral_init(); + +#ifdef CONFIG_OMAP_OSK_MISTRAL + omapfb_set_lcd_config(&osk_lcd_config); +#endif + +} + +MACHINE_START(OMAP_OSK, "TI-OSK") + /* Maintainer: Dirk Behme <dirk.behme@de.bosch.com> */ + .atag_offset = 0x100, + .map_io = omap16xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = osk_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c new file mode 100644 index 000000000..72e1979c7 --- /dev/null +++ b/arch/arm/mach-omap1/board-palmte.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-palmte.c + * + * Modified from board-generic.c + * + * Support for the Palm Tungsten E PDA. + * + * Original version : Laurent Gonzalez + * + * Maintainers : http://palmtelinux.sf.net + * palmtelinux-developpers@lists.sf.net + * + * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> + */ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/spi/spi.h> +#include <linux/interrupt.h> +#include <linux/apm-emulation.h> +#include <linux/omapfb.h> +#include <linux/omap-dma.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/platform_data/omap1_bl.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "flash.h" +#include "mux.h" +#include "hardware.h" +#include "usb.h" +#include "mmc.h" +#include "common.h" + +#define PALMTE_USBDETECT_GPIO 0 +#define PALMTE_USB_OR_DC_GPIO 1 +#define PALMTE_TSC_GPIO 4 +#define PALMTE_PINTDAV_GPIO 6 +#define PALMTE_MMC_WP_GPIO 8 +#define PALMTE_MMC_POWER_GPIO 9 +#define PALMTE_HDQ_GPIO 11 +#define PALMTE_HEADPHONES_GPIO 14 +#define PALMTE_SPEAKER_GPIO 15 +#define PALMTE_DC_GPIO OMAP_MPUIO(2) +#define PALMTE_MMC_SWITCH_GPIO OMAP_MPUIO(4) +#define PALMTE_MMC1_GPIO OMAP_MPUIO(6) +#define PALMTE_MMC2_GPIO OMAP_MPUIO(7) +#define PALMTE_MMC3_GPIO OMAP_MPUIO(11) + +static const unsigned int palmte_keymap[] = { + KEY(0, 0, KEY_F1), /* Calendar */ + KEY(1, 0, KEY_F2), /* Contacts */ + KEY(2, 0, KEY_F3), /* Tasks List */ + KEY(3, 0, KEY_F4), /* Note Pad */ + KEY(4, 0, KEY_POWER), + KEY(0, 1, KEY_LEFT), + KEY(1, 1, KEY_DOWN), + KEY(2, 1, KEY_UP), + KEY(3, 1, KEY_RIGHT), + KEY(4, 1, KEY_ENTER), +}; + +static const struct matrix_keymap_data palmte_keymap_data = { + .keymap = palmte_keymap, + .keymap_size = ARRAY_SIZE(palmte_keymap), +}; + +static struct omap_kp_platform_data palmte_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &palmte_keymap_data, + .rep = true, + .delay = 12, +}; + +static struct resource palmte_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device palmte_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &palmte_kp_data, + }, + .num_resources = ARRAY_SIZE(palmte_kp_resources), + .resource = palmte_kp_resources, +}; + +static struct mtd_partition palmte_rom_partitions[] = { + /* PalmOS "Small ROM", contains the bootloader and the debugger */ + { + .name = "smallrom", + .offset = 0, + .size = 0xa000, + .mask_flags = MTD_WRITEABLE, + }, + /* PalmOS "Big ROM", a filesystem with all the OS code and data */ + { + .name = "bigrom", + .offset = SZ_128K, + /* + * 0x5f0000 bytes big in the multi-language ("EFIGS") version, + * 0x7b0000 bytes in the English-only ("enUS") version. + */ + .size = 0x7b0000, + .mask_flags = MTD_WRITEABLE, + }, +}; + +static struct physmap_flash_data palmte_rom_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = palmte_rom_partitions, + .nr_parts = ARRAY_SIZE(palmte_rom_partitions), +}; + +static struct resource palmte_rom_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_8M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device palmte_rom_device = { + .name = "physmap-flash", + .id = -1, + .dev = { + .platform_data = &palmte_rom_data, + }, + .num_resources = 1, + .resource = &palmte_rom_resource, +}; + +static struct platform_device palmte_lcd_device = { + .name = "lcd_palmte", + .id = -1, +}; + +static struct omap_backlight_config palmte_backlight_config = { + .default_intensity = 0xa0, +}; + +static struct platform_device palmte_backlight_device = { + .name = "omap-bl", + .id = -1, + .dev = { + .platform_data = &palmte_backlight_config, + }, +}; + +static struct platform_device *palmte_devices[] __initdata = { + &palmte_rom_device, + &palmte_kp_device, + &palmte_lcd_device, + &palmte_backlight_device, +}; + +static struct omap_usb_config palmte_usb_config __initdata = { + .register_dev = 1, /* Mini-B only receptacle */ + .hmc_mode = 0, + .pins[0] = 2, +}; + +static const struct omap_lcd_config palmte_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static struct spi_board_info palmte_spi_info[] __initdata = { + { + .modalias = "tsc2102", + .bus_num = 2, /* uWire (officially) */ + .chip_select = 0, /* As opposed to 3 */ + .max_speed_hz = 8000000, + }, +}; + +static void __init palmte_misc_gpio_setup(void) +{ + /* Set TSC2102 PINTDAV pin as input (used by TSC2102 driver) */ + if (gpio_request(PALMTE_PINTDAV_GPIO, "TSC2102 PINTDAV") < 0) { + printk(KERN_ERR "Could not reserve PINTDAV GPIO!\n"); + return; + } + gpio_direction_input(PALMTE_PINTDAV_GPIO); + + /* Set USB-or-DC-IN pin as input (unused) */ + if (gpio_request(PALMTE_USB_OR_DC_GPIO, "USB/DC-IN") < 0) { + printk(KERN_ERR "Could not reserve cable signal GPIO!\n"); + return; + } + gpio_direction_input(PALMTE_USB_OR_DC_GPIO); +} + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static struct omap_mmc_platform_data _palmte_mmc_config = { + .nr_slots = 1, + .slots[0] = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *palmte_mmc_config[OMAP15XX_NR_MMC] = { + [0] = &_palmte_mmc_config, +}; + +static void palmte_mmc_init(void) +{ + omap1_init_mmc(palmte_mmc_config, OMAP15XX_NR_MMC); +} + +#else /* CONFIG_MMC_OMAP */ + +static void palmte_mmc_init(void) +{ +} + +#endif /* CONFIG_MMC_OMAP */ + +static void __init omap_palmte_init(void) +{ + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + platform_add_devices(palmte_devices, ARRAY_SIZE(palmte_devices)); + + palmte_spi_info[0].irq = gpio_to_irq(PALMTE_PINTDAV_GPIO); + spi_register_board_info(palmte_spi_info, ARRAY_SIZE(palmte_spi_info)); + palmte_misc_gpio_setup(); + omap_serial_init(); + omap1_usb_init(&palmte_usb_config); + omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&palmte_lcd_config); + palmte_mmc_init(); +} + +MACHINE_START(OMAP_PALMTE, "OMAP310 based Palm Tungsten E") + .atag_offset = 0x100, + .map_io = omap15xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_palmte_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c new file mode 100644 index 000000000..537f0e6a2 --- /dev/null +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-palmtt.c + * + * Modified from board-palmtt2.c + * + * Modified and amended for Palm Tungsten|T + * by Marek Vasut <marek.vasut@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/notifier.h> +#include <linux/clk.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/leds.h> +#include <linux/omapfb.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> +#include <linux/omap-dma.h> +#include <linux/platform_data/omap1_bl.h> +#include <linux/platform_data/leds-omap.h> +#include <linux/platform_data/keypad-omap.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "flash.h" +#include "mux.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" + +#define PALMTT_USBDETECT_GPIO 0 +#define PALMTT_CABLE_GPIO 1 +#define PALMTT_LED_GPIO 3 +#define PALMTT_PENIRQ_GPIO 6 +#define PALMTT_MMC_WP_GPIO 8 +#define PALMTT_HDQ_GPIO 11 + +static const unsigned int palmtt_keymap[] = { + KEY(0, 0, KEY_ESC), + KEY(1, 0, KEY_SPACE), + KEY(2, 0, KEY_LEFTCTRL), + KEY(3, 0, KEY_TAB), + KEY(4, 0, KEY_ENTER), + KEY(0, 1, KEY_LEFT), + KEY(1, 1, KEY_DOWN), + KEY(2, 1, KEY_UP), + KEY(3, 1, KEY_RIGHT), + KEY(0, 2, KEY_SLEEP), + KEY(4, 2, KEY_Y), +}; + +static struct mtd_partition palmtt_partitions[] = { + { + .name = "write8k", + .offset = 0, + .size = SZ_8K, + .mask_flags = 0, + }, + { + .name = "PalmOS-BootLoader(ro)", + .offset = SZ_8K, + .size = 7 * SZ_8K, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "u-boot", + .offset = MTDPART_OFS_APPEND, + .size = 8 * SZ_8K, + .mask_flags = 0, + }, + { + .name = "PalmOS-FS(ro)", + .offset = MTDPART_OFS_APPEND, + .size = 7 * SZ_1M + 4 * SZ_64K - 16 * SZ_8K, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "u-boot(rez)", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0 + }, + { + .name = "empty", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data palmtt_flash_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = palmtt_partitions, + .nr_parts = ARRAY_SIZE(palmtt_partitions), +}; + +static struct resource palmtt_flash_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_8M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device palmtt_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &palmtt_flash_data, + }, + .num_resources = 1, + .resource = &palmtt_flash_resource, +}; + +static struct resource palmtt_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data palmtt_keymap_data = { + .keymap = palmtt_keymap, + .keymap_size = ARRAY_SIZE(palmtt_keymap), +}; + +static struct omap_kp_platform_data palmtt_kp_data = { + .rows = 6, + .cols = 3, + .keymap_data = &palmtt_keymap_data, +}; + +static struct platform_device palmtt_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &palmtt_kp_data, + }, + .num_resources = ARRAY_SIZE(palmtt_kp_resources), + .resource = palmtt_kp_resources, +}; + +static struct platform_device palmtt_lcd_device = { + .name = "lcd_palmtt", + .id = -1, +}; + +static struct platform_device palmtt_spi_device = { + .name = "spi_palmtt", + .id = -1, +}; + +static struct omap_backlight_config palmtt_backlight_config = { + .default_intensity = 0xa0, +}; + +static struct platform_device palmtt_backlight_device = { + .name = "omap-bl", + .id = -1, + .dev = { + .platform_data= &palmtt_backlight_config, + }, +}; + +static struct omap_led_config palmtt_led_config[] = { + { + .cdev = { + .name = "palmtt:led0", + }, + .gpio = PALMTT_LED_GPIO, + }, +}; + +static struct omap_led_platform_data palmtt_led_data = { + .nr_leds = ARRAY_SIZE(palmtt_led_config), + .leds = palmtt_led_config, +}; + +static struct platform_device palmtt_led_device = { + .name = "omap-led", + .id = -1, + .dev = { + .platform_data = &palmtt_led_data, + }, +}; + +static struct platform_device *palmtt_devices[] __initdata = { + &palmtt_flash_device, + &palmtt_kp_device, + &palmtt_lcd_device, + &palmtt_spi_device, + &palmtt_backlight_device, + &palmtt_led_device, +}; + +static int palmtt_get_pendown_state(void) +{ + return !gpio_get_value(6); +} + +static const struct ads7846_platform_data palmtt_ts_info = { + .model = 7846, + .vref_delay_usecs = 100, /* internal, no capacitor */ + .x_plate_ohms = 419, + .y_plate_ohms = 486, + .get_pendown_state = palmtt_get_pendown_state, +}; + +static struct spi_board_info __initdata palmtt_boardinfo[] = { + { + /* MicroWire (bus 2) CS0 has an ads7846e */ + .modalias = "ads7846", + .platform_data = &palmtt_ts_info, + .max_speed_hz = 120000 /* max sample rate at 3V */ + * 26 /* command + data + overhead */, + .bus_num = 2, + .chip_select = 0, + } +}; + +static struct omap_usb_config palmtt_usb_config __initdata = { + .register_dev = 1, + .hmc_mode = 0, + .pins[0] = 2, +}; + +static const struct omap_lcd_config palmtt_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static void __init omap_mpu_wdt_mode(int mode) { + if (mode) + omap_writew(0x8000, OMAP_WDT_TIMER_MODE); + else { + omap_writew(0x00f5, OMAP_WDT_TIMER_MODE); + omap_writew(0x00a0, OMAP_WDT_TIMER_MODE); + } +} + +static void __init omap_palmtt_init(void) +{ + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + omap_mpu_wdt_mode(0); + + platform_add_devices(palmtt_devices, ARRAY_SIZE(palmtt_devices)); + + palmtt_boardinfo[0].irq = gpio_to_irq(6); + spi_register_board_info(palmtt_boardinfo,ARRAY_SIZE(palmtt_boardinfo)); + omap_serial_init(); + omap1_usb_init(&palmtt_usb_config); + omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&palmtt_lcd_config); +} + +MACHINE_START(OMAP_PALMTT, "OMAP1510 based Palm Tungsten|T") + .atag_offset = 0x100, + .map_io = omap15xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_palmtt_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c new file mode 100644 index 000000000..47f08ae5a --- /dev/null +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-palmz71.c + * + * Modified from board-generic.c + * + * Support for the Palm Zire71 PDA. + * + * Original version : Laurent Gonzalez + * + * Modified for zire71 : Marek Vasut + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/notifier.h> +#include <linux/clk.h> +#include <linux/irq.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/omapfb.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> +#include <linux/platform_data/omap1_bl.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/omap-dma.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "flash.h" +#include "mux.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" + +#define PALMZ71_USBDETECT_GPIO 0 +#define PALMZ71_PENIRQ_GPIO 6 +#define PALMZ71_MMC_WP_GPIO 8 +#define PALMZ71_HDQ_GPIO 11 + +#define PALMZ71_HOTSYNC_GPIO OMAP_MPUIO(1) +#define PALMZ71_CABLE_GPIO OMAP_MPUIO(2) +#define PALMZ71_SLIDER_GPIO OMAP_MPUIO(3) +#define PALMZ71_MMC_IN_GPIO OMAP_MPUIO(4) + +static const unsigned int palmz71_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(1, 0, KEY_F2), + KEY(2, 0, KEY_F3), + KEY(3, 0, KEY_F4), + KEY(4, 0, KEY_POWER), + KEY(0, 1, KEY_LEFT), + KEY(1, 1, KEY_DOWN), + KEY(2, 1, KEY_UP), + KEY(3, 1, KEY_RIGHT), + KEY(4, 1, KEY_ENTER), + KEY(0, 2, KEY_CAMERA), +}; + +static const struct matrix_keymap_data palmz71_keymap_data = { + .keymap = palmz71_keymap, + .keymap_size = ARRAY_SIZE(palmz71_keymap), +}; + +static struct omap_kp_platform_data palmz71_kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &palmz71_keymap_data, + .rep = true, + .delay = 80, +}; + +static struct resource palmz71_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device palmz71_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &palmz71_kp_data, + }, + .num_resources = ARRAY_SIZE(palmz71_kp_resources), + .resource = palmz71_kp_resources, +}; + +static struct mtd_partition palmz71_rom_partitions[] = { + /* PalmOS "Small ROM", contains the bootloader and the debugger */ + { + .name = "smallrom", + .offset = 0, + .size = 0xa000, + .mask_flags = MTD_WRITEABLE, + }, + /* PalmOS "Big ROM", a filesystem with all the OS code and data */ + { + .name = "bigrom", + .offset = SZ_128K, + /* + * 0x5f0000 bytes big in the multi-language ("EFIGS") version, + * 0x7b0000 bytes in the English-only ("enUS") version. + */ + .size = 0x7b0000, + .mask_flags = MTD_WRITEABLE, + }, +}; + +static struct physmap_flash_data palmz71_rom_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = palmz71_rom_partitions, + .nr_parts = ARRAY_SIZE(palmz71_rom_partitions), +}; + +static struct resource palmz71_rom_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_8M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device palmz71_rom_device = { + .name = "physmap-flash", + .id = -1, + .dev = { + .platform_data = &palmz71_rom_data, + }, + .num_resources = 1, + .resource = &palmz71_rom_resource, +}; + +static struct platform_device palmz71_lcd_device = { + .name = "lcd_palmz71", + .id = -1, +}; + +static struct platform_device palmz71_spi_device = { + .name = "spi_palmz71", + .id = -1, +}; + +static struct omap_backlight_config palmz71_backlight_config = { + .default_intensity = 0xa0, +}; + +static struct platform_device palmz71_backlight_device = { + .name = "omap-bl", + .id = -1, + .dev = { + .platform_data = &palmz71_backlight_config, + }, +}; + +static struct platform_device *devices[] __initdata = { + &palmz71_rom_device, + &palmz71_kp_device, + &palmz71_lcd_device, + &palmz71_spi_device, + &palmz71_backlight_device, +}; + +static int +palmz71_get_pendown_state(void) +{ + return !gpio_get_value(PALMZ71_PENIRQ_GPIO); +} + +static const struct ads7846_platform_data palmz71_ts_info = { + .model = 7846, + .vref_delay_usecs = 100, /* internal, no capacitor */ + .x_plate_ohms = 419, + .y_plate_ohms = 486, + .get_pendown_state = palmz71_get_pendown_state, +}; + +static struct spi_board_info __initdata palmz71_boardinfo[] = { { + /* MicroWire (bus 2) CS0 has an ads7846e */ + .modalias = "ads7846", + .platform_data = &palmz71_ts_info, + .max_speed_hz = 120000 /* max sample rate at 3V */ + * 26 /* command + data + overhead */, + .bus_num = 2, + .chip_select = 0, +} }; + +static struct omap_usb_config palmz71_usb_config __initdata = { + .register_dev = 1, /* Mini-B only receptacle */ + .hmc_mode = 0, + .pins[0] = 2, +}; + +static const struct omap_lcd_config palmz71_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static irqreturn_t +palmz71_powercable(int irq, void *dev_id) +{ + if (gpio_get_value(PALMZ71_USBDETECT_GPIO)) { + printk(KERN_INFO "PM: Power cable connected\n"); + irq_set_irq_type(gpio_to_irq(PALMZ71_USBDETECT_GPIO), + IRQ_TYPE_EDGE_FALLING); + } else { + printk(KERN_INFO "PM: Power cable disconnected\n"); + irq_set_irq_type(gpio_to_irq(PALMZ71_USBDETECT_GPIO), + IRQ_TYPE_EDGE_RISING); + } + return IRQ_HANDLED; +} + +static void __init +omap_mpu_wdt_mode(int mode) +{ + if (mode) + omap_writew(0x8000, OMAP_WDT_TIMER_MODE); + else { + omap_writew(0x00f5, OMAP_WDT_TIMER_MODE); + omap_writew(0x00a0, OMAP_WDT_TIMER_MODE); + } +} + +static void __init +palmz71_gpio_setup(int early) +{ + if (early) { + /* Only set GPIO1 so we have a working serial */ + gpio_direction_output(1, 1); + } else { + /* Set MMC/SD host WP pin as input */ + if (gpio_request(PALMZ71_MMC_WP_GPIO, "MMC WP") < 0) { + printk(KERN_ERR "Could not reserve WP GPIO!\n"); + return; + } + gpio_direction_input(PALMZ71_MMC_WP_GPIO); + + /* Monitor the Power-cable-connected signal */ + if (gpio_request(PALMZ71_USBDETECT_GPIO, "USB detect") < 0) { + printk(KERN_ERR + "Could not reserve cable signal GPIO!\n"); + return; + } + gpio_direction_input(PALMZ71_USBDETECT_GPIO); + if (request_irq(gpio_to_irq(PALMZ71_USBDETECT_GPIO), + palmz71_powercable, 0, "palmz71-cable", NULL)) + printk(KERN_ERR + "IRQ request for power cable failed!\n"); + palmz71_powercable(gpio_to_irq(PALMZ71_USBDETECT_GPIO), NULL); + } +} + +static void __init +omap_palmz71_init(void) +{ + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + palmz71_gpio_setup(1); + omap_mpu_wdt_mode(0); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + palmz71_boardinfo[0].irq = gpio_to_irq(PALMZ71_PENIRQ_GPIO); + spi_register_board_info(palmz71_boardinfo, + ARRAY_SIZE(palmz71_boardinfo)); + omap1_usb_init(&palmz71_usb_config); + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + palmz71_gpio_setup(0); + + omapfb_set_lcd_config(&palmz71_lcd_config); +} + +MACHINE_START(OMAP_PALMZ71, "OMAP310 based Palm Zire71") + .atag_offset = 0x100, + .map_io = omap15xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_palmz71_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c new file mode 100644 index 000000000..b041e6f6e --- /dev/null +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-perseus2.c + * + * Modified from board-generic.c + * + * Original OMAP730 support by Jean Pihet <j-pihet@ti.com> + * Updated for 2.6 by Kevin Hilman <kjh@hilman.org> + */ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/platnand.h> +#include <linux/mtd/physmap.h> +#include <linux/input.h> +#include <linux/smc91x.h> +#include <linux/omapfb.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "mux.h" +#include "flash.h" +#include "hardware.h" +#include "iomap.h" +#include "common.h" +#include "fpga.h" + +static const unsigned int p2_keymap[] = { + KEY(0, 0, KEY_UP), + KEY(1, 0, KEY_RIGHT), + KEY(2, 0, KEY_LEFT), + KEY(3, 0, KEY_DOWN), + KEY(4, 0, KEY_ENTER), + KEY(0, 1, KEY_F10), + KEY(1, 1, KEY_SEND), + KEY(2, 1, KEY_END), + KEY(3, 1, KEY_VOLUMEDOWN), + KEY(4, 1, KEY_VOLUMEUP), + KEY(5, 1, KEY_RECORD), + KEY(0, 2, KEY_F9), + KEY(1, 2, KEY_3), + KEY(2, 2, KEY_6), + KEY(3, 2, KEY_9), + KEY(4, 2, KEY_KPDOT), + KEY(0, 3, KEY_BACK), + KEY(1, 3, KEY_2), + KEY(2, 3, KEY_5), + KEY(3, 3, KEY_8), + KEY(4, 3, KEY_0), + KEY(5, 3, KEY_KPSLASH), + KEY(0, 4, KEY_HOME), + KEY(1, 4, KEY_1), + KEY(2, 4, KEY_4), + KEY(3, 4, KEY_7), + KEY(4, 4, KEY_KPASTERISK), + KEY(5, 4, KEY_POWER), +}; + +static struct smc91x_platdata smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .leda = RPC_LED_100_10, + .ledb = RPC_LED_TX_RX, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ + .end = H2P2_DBG_FPGA_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_7XX_MPU_EXT_NIRQ, + .end = 0, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + }, +}; + +static struct mtd_partition nor_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* rest of flash is a file system */ + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + }, +}; + +static struct physmap_flash_data nor_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), +}; + +static struct resource nor_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nor_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &nor_data, + }, + .num_resources = 1, + .resource = &nor_resource, +}; + +#define P2_NAND_RB_GPIO_PIN 62 + +static int nand_dev_ready(struct nand_chip *chip) +{ + return gpio_get_value(P2_NAND_RB_GPIO_PIN); +} + +static struct platform_nand_data nand_data = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .options = NAND_SAMSUNG_LP_OPTIONS, + }, + .ctrl = { + .cmd_ctrl = omap1_nand_cmd_ctl, + .dev_ready = nand_dev_ready, + }, +}; + +static struct resource nand_resource = { + .start = OMAP_CS3_PHYS, + .end = OMAP_CS3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "gen_nand", + .id = 0, + .dev = { + .platform_data = &nand_data, + }, + .num_resources = 1, + .resource = &nand_resource, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &smc91x_info, + }, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct resource kp_resources[] = { + [0] = { + .start = INT_7XX_MPUIO_KEYPAD, + .end = INT_7XX_MPUIO_KEYPAD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data p2_keymap_data = { + .keymap = p2_keymap, + .keymap_size = ARRAY_SIZE(p2_keymap), +}; + +static struct omap_kp_platform_data kp_data = { + .rows = 8, + .cols = 8, + .keymap_data = &p2_keymap_data, + .delay = 4, + .dbounce = true, +}; + +static struct platform_device kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &kp_data, + }, + .num_resources = ARRAY_SIZE(kp_resources), + .resource = kp_resources, +}; + +static struct platform_device *devices[] __initdata = { + &nor_device, + &nand_device, + &smc91x_device, + &kp_device, +}; + +static const struct omap_lcd_config perseus2_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +static void __init perseus2_init_smc91x(void) +{ + __raw_writeb(1, H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); + __raw_writeb(__raw_readb(H2P2_DBG_FPGA_LAN_RESET) & ~1, + H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); +} + +static void __init omap_perseus2_init(void) +{ + /* Early, board-dependent init */ + + /* + * Hold GSM Reset until needed + */ + omap_writew(omap_readw(OMAP7XX_DSP_M_CTL) & ~1, OMAP7XX_DSP_M_CTL); + + /* + * UARTs -> done automagically by 8250 driver + */ + + /* + * CSx timings, GPIO Mux ... setup + */ + + /* Flash: CS0 timings setup */ + omap_writel(0x0000fff3, OMAP7XX_FLASH_CFG_0); + omap_writel(0x00000088, OMAP7XX_FLASH_ACFG_0); + + /* + * Ethernet support through the debug board + * CS1 timings setup + */ + omap_writel(0x0000fff3, OMAP7XX_FLASH_CFG_1); + omap_writel(0x00000000, OMAP7XX_FLASH_ACFG_1); + + /* + * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, + * It is used as the Ethernet controller interrupt + */ + omap_writel(omap_readl(OMAP7XX_IO_CONF_9) & 0x1FFFFFFF, + OMAP7XX_IO_CONF_9); + + perseus2_init_smc91x(); + + BUG_ON(gpio_request(P2_NAND_RB_GPIO_PIN, "NAND ready") < 0); + gpio_direction_input(P2_NAND_RB_GPIO_PIN); + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); + + /* Mux pins for keypad */ + omap_cfg_reg(E2_7XX_KBR0); + omap_cfg_reg(J7_7XX_KBR1); + omap_cfg_reg(E1_7XX_KBR2); + omap_cfg_reg(F3_7XX_KBR3); + omap_cfg_reg(D2_7XX_KBR4); + omap_cfg_reg(C2_7XX_KBC0); + omap_cfg_reg(D3_7XX_KBC1); + omap_cfg_reg(E4_7XX_KBC2); + omap_cfg_reg(F4_7XX_KBC3); + omap_cfg_reg(E3_7XX_KBC4); + + if (IS_ENABLED(CONFIG_SPI_OMAP_UWIRE)) { + /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */ + int val = omap_readl(OMAP7XX_IO_CONF_9) & ~0x00EEE000; + omap_writel(val | 0x00AAA000, OMAP7XX_IO_CONF_9); + } + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + + omapfb_set_lcd_config(&perseus2_lcd_config); +} + +/* Only FPGA needs to be mapped here. All others are done with ioremap */ +static struct map_desc omap_perseus2_io_desc[] __initdata = { + { + .virtual = H2P2_DBG_FPGA_BASE, + .pfn = __phys_to_pfn(H2P2_DBG_FPGA_START), + .length = H2P2_DBG_FPGA_SIZE, + .type = MT_DEVICE + } +}; + +static void __init omap_perseus2_map_io(void) +{ + omap7xx_map_io(); + iotable_init(omap_perseus2_io_desc, + ARRAY_SIZE(omap_perseus2_io_desc)); +} + +MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2") + /* Maintainer: Kevin Hilman <kjh@hilman.org> */ + .atag_offset = 0x100, + .map_io = omap_perseus2_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_perseus2_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-sx1-mmc.c b/arch/arm/mach-omap1/board-sx1-mmc.c new file mode 100644 index 000000000..f1c160924 --- /dev/null +++ b/arch/arm/mach-omap1/board-sx1-mmc.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/board-sx1-mmc.c + * + * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT + * Author: Carlos Eduardo Aguiar <carlos.aguiar@indt.org.br> + * + * This code is based on linux/arch/arm/mach-omap1/board-h2-mmc.c, which is: + * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT + */ + +#include <linux/gpio.h> +#include <linux/platform_device.h> + +#include "hardware.h" +#include "board-sx1.h" +#include "mmc.h" + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static int mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + int err; + u8 dat = 0; + + err = sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); + if (err < 0) + return err; + + if (power_on) + dat |= SOFIA_MMC_POWER; + else + dat &= ~SOFIA_MMC_POWER; + + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); +} + +/* Cover switch is at OMAP_MPUIO(3) */ +static struct omap_mmc_platform_data mmc1_data = { + .nr_slots = 1, + .slots[0] = { + .set_power = mmc_set_power, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +static struct omap_mmc_platform_data *mmc_data[OMAP15XX_NR_MMC]; + +void __init sx1_mmc_init(void) +{ + mmc_data[0] = &mmc1_data; + omap1_init_mmc(mmc_data, OMAP15XX_NR_MMC); +} + +#else + +void __init sx1_mmc_init(void) +{ +} + +#endif diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c new file mode 100644 index 000000000..f0dbb0e8d --- /dev/null +++ b/arch/arm/mach-omap1/board-sx1.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* linux/arch/arm/mach-omap1/board-sx1.c +* +* Modified from board-generic.c +* +* Support for the Siemens SX1 mobile phone. +* +* Original version : Vladimir Ananiev (Vovan888-at-gmail com) +* +* Maintainters : Vladimir Ananiev (aka Vovan888), Sergge +* oslik.ru +*/ +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/notifier.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/omapfb.h> +#include <linux/platform_data/keypad-omap.h> +#include <linux/omap-dma.h> +#include "tc.h" + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "flash.h" +#include "mux.h" +#include "board-sx1.h" +#include "hardware.h" +#include "usb.h" +#include "common.h" + +/* Write to I2C device */ +int sx1_i2c_write_byte(u8 devaddr, u8 regoffset, u8 value) +{ + struct i2c_adapter *adap; + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + adap = i2c_get_adapter(0); + if (!adap) + return -ENODEV; + msg->addr = devaddr; /* I2C address of chip */ + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = regoffset; /* register num */ + data[1] = value; /* register data */ + err = i2c_transfer(adap, msg, 1); + i2c_put_adapter(adap); + if (err >= 0) + return 0; + return err; +} + +/* Read from I2C device */ +int sx1_i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value) +{ + struct i2c_adapter *adap; + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + adap = i2c_get_adapter(0); + if (!adap) + return -ENODEV; + + msg->addr = devaddr; /* I2C address of chip */ + msg->flags = 0; + msg->len = 1; + msg->buf = data; + data[0] = regoffset; /* register num */ + err = i2c_transfer(adap, msg, 1); + + msg->addr = devaddr; /* I2C address */ + msg->flags = I2C_M_RD; + msg->len = 1; + msg->buf = data; + err = i2c_transfer(adap, msg, 1); + *value = data[0]; + i2c_put_adapter(adap); + + if (err >= 0) + return 0; + return err; +} +/* set keyboard backlight intensity */ +int sx1_setkeylight(u8 keylight) +{ + if (keylight > SOFIA_MAX_LIGHT_VAL) + keylight = SOFIA_MAX_LIGHT_VAL; + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_KEYLIGHT_REG, keylight); +} +/* get current keylight intensity */ +int sx1_getkeylight(u8 * keylight) +{ + return sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_KEYLIGHT_REG, keylight); +} +/* set LCD backlight intensity */ +int sx1_setbacklight(u8 backlight) +{ + if (backlight > SOFIA_MAX_LIGHT_VAL) + backlight = SOFIA_MAX_LIGHT_VAL; + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_BACKLIGHT_REG, + backlight); +} +/* get current LCD backlight intensity */ +int sx1_getbacklight (u8 * backlight) +{ + return sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_BACKLIGHT_REG, + backlight); +} +/* set LCD backlight power on/off */ +int sx1_setmmipower(u8 onoff) +{ + int err; + u8 dat = 0; + err = sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); + if (err < 0) + return err; + if (onoff) + dat |= SOFIA_MMILIGHT_POWER; + else + dat &= ~SOFIA_MMILIGHT_POWER; + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); +} + +/* set USB power on/off */ +int sx1_setusbpower(u8 onoff) +{ + int err; + u8 dat = 0; + err = sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); + if (err < 0) + return err; + if (onoff) + dat |= SOFIA_USB_POWER; + else + dat &= ~SOFIA_USB_POWER; + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); +} + +EXPORT_SYMBOL(sx1_setkeylight); +EXPORT_SYMBOL(sx1_getkeylight); +EXPORT_SYMBOL(sx1_setbacklight); +EXPORT_SYMBOL(sx1_getbacklight); +EXPORT_SYMBOL(sx1_setmmipower); +EXPORT_SYMBOL(sx1_setusbpower); + +/*----------- Keypad -------------------------*/ + +static const unsigned int sx1_keymap[] = { + KEY(3, 5, GROUP_0 | 117), /* camera Qt::Key_F17 */ + KEY(4, 0, GROUP_0 | 114), /* voice memo Qt::Key_F14 */ + KEY(4, 1, GROUP_2 | 114), /* voice memo */ + KEY(4, 2, GROUP_3 | 114), /* voice memo */ + KEY(0, 0, GROUP_1 | KEY_F12), /* red button Qt::Key_Hangup */ + KEY(3, 4, GROUP_1 | KEY_LEFT), + KEY(3, 2, GROUP_1 | KEY_DOWN), + KEY(3, 1, GROUP_1 | KEY_RIGHT), + KEY(3, 0, GROUP_1 | KEY_UP), + KEY(3, 3, GROUP_1 | KEY_POWER), /* joystick press or Qt::Key_Select */ + KEY(0, 5, GROUP_1 | KEY_1), + KEY(0, 4, GROUP_1 | KEY_2), + KEY(0, 3, GROUP_1 | KEY_3), + KEY(4, 3, GROUP_1 | KEY_4), + KEY(4, 4, GROUP_1 | KEY_5), + KEY(4, 5, GROUP_1 | KEY_KPASTERISK),/* "*" */ + KEY(1, 4, GROUP_1 | KEY_6), + KEY(1, 5, GROUP_1 | KEY_7), + KEY(1, 3, GROUP_1 | KEY_8), + KEY(2, 3, GROUP_1 | KEY_9), + KEY(2, 5, GROUP_1 | KEY_0), + KEY(2, 4, GROUP_1 | 113), /* # F13 Toggle input method Qt::Key_F13 */ + KEY(1, 0, GROUP_1 | KEY_F11), /* green button Qt::Key_Call */ + KEY(2, 1, GROUP_1 | KEY_YEN), /* left soft Qt::Key_Context1 */ + KEY(2, 2, GROUP_1 | KEY_F8), /* right soft Qt::Key_Back */ + KEY(1, 2, GROUP_1 | KEY_LEFTSHIFT), /* shift */ + KEY(1, 1, GROUP_1 | KEY_BACKSPACE), /* C (clear) */ + KEY(2, 0, GROUP_1 | KEY_F7), /* menu Qt::Key_Menu */ +}; + +static struct resource sx1_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct matrix_keymap_data sx1_keymap_data = { + .keymap = sx1_keymap, + .keymap_size = ARRAY_SIZE(sx1_keymap), +}; + +static struct omap_kp_platform_data sx1_kp_data = { + .rows = 6, + .cols = 6, + .keymap_data = &sx1_keymap_data, + .delay = 80, +}; + +static struct platform_device sx1_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &sx1_kp_data, + }, + .num_resources = ARRAY_SIZE(sx1_kp_resources), + .resource = sx1_kp_resources, +}; + +/*----------- MTD -------------------------*/ + +static struct mtd_partition sx1_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0x01800000, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M - 2 * SZ_128K, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct physmap_flash_data sx1_flash_data = { + .width = 2, + .set_vpp = omap1_set_vpp, + .parts = sx1_partitions, + .nr_parts = ARRAY_SIZE(sx1_partitions), +}; + +/* MTD Intel 4000 flash - new flashes */ +static struct resource sx1_new_flash_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device sx1_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &sx1_flash_data, + }, + .num_resources = 1, + .resource = &sx1_new_flash_resource, +}; + +/*----------- USB -------------------------*/ + +static struct omap_usb_config sx1_usb_config __initdata = { + .otg = 0, + .register_dev = 1, + .register_host = 0, + .hmc_mode = 0, + .pins[0] = 2, + .pins[1] = 0, + .pins[2] = 0, +}; + +/*----------- LCD -------------------------*/ + +static const struct omap_lcd_config sx1_lcd_config __initconst = { + .ctrl_name = "internal", +}; + +/*-----------------------------------------*/ +static struct platform_device *sx1_devices[] __initdata = { + &sx1_flash_device, + &sx1_kp_device, +}; + +/*-----------------------------------------*/ + +static void __init omap_sx1_init(void) +{ + /* mux pins for uarts */ + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + + platform_add_devices(sx1_devices, ARRAY_SIZE(sx1_devices)); + + omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + omap1_usb_init(&sx1_usb_config); + sx1_mmc_init(); + + /* turn on USB power */ + /* sx1_setusbpower(1); can't do it here because i2c is not ready */ + gpio_request(1, "A_IRDA_OFF"); + gpio_request(11, "A_SWITCH"); + gpio_request(15, "A_USB_ON"); + gpio_direction_output(1, 1); /*A_IRDA_OFF = 1 */ + gpio_direction_output(11, 0); /*A_SWITCH = 0 */ + gpio_direction_output(15, 0); /*A_USB_ON = 0 */ + + omapfb_set_lcd_config(&sx1_lcd_config); +} + +MACHINE_START(SX1, "OMAP310 based Siemens SX1") + .atag_offset = 0x100, + .map_io = omap15xx_map_io, + .init_early = omap1_init_early, + .init_irq = omap1_init_irq, + .handle_irq = omap1_handle_irq, + .init_machine = omap_sx1_init, + .init_late = omap1_init_late, + .init_time = omap1_timer_init, + .restart = omap1_restart, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-sx1.h b/arch/arm/mach-omap1/board-sx1.h new file mode 100644 index 000000000..fafe54a2e --- /dev/null +++ b/arch/arm/mach-omap1/board-sx1.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Siemens SX1 board definitions + * + * Copyright: Vovan888 at gmail com + */ + +#ifndef __ASM_ARCH_SX1_I2C_CHIPS_H +#define __ASM_ARCH_SX1_I2C_CHIPS_H + +#define SOFIA_MAX_LIGHT_VAL 0x2B + +#define SOFIA_I2C_ADDR 0x32 +/* Sofia reg 3 bits masks */ +#define SOFIA_POWER1_REG 0x03 + +#define SOFIA_USB_POWER 0x01 +#define SOFIA_MMC_POWER 0x04 +#define SOFIA_BLUETOOTH_POWER 0x08 +#define SOFIA_MMILIGHT_POWER 0x20 + +#define SOFIA_POWER2_REG 0x04 +#define SOFIA_BACKLIGHT_REG 0x06 +#define SOFIA_KEYLIGHT_REG 0x07 +#define SOFIA_DIMMING_REG 0x09 + + +/* Function Prototypes for SX1 devices control on I2C bus */ + +int sx1_setbacklight(u8 backlight); +int sx1_getbacklight(u8 *backlight); +int sx1_setkeylight(u8 keylight); +int sx1_getkeylight(u8 *keylight); + +int sx1_setmmipower(u8 onoff); +int sx1_setusbpower(u8 onoff); +int sx1_i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value); +int sx1_i2c_write_byte(u8 devaddr, u8 regoffset, u8 value); + +/* MMC prototypes */ + +extern void sx1_mmc_init(void); +extern void sx1_mmc_slot_cover_handler(void *arg, int state); + +#endif /* __ASM_ARCH_SX1_I2C_CHIPS_H */ diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c new file mode 100644 index 000000000..83381e23f --- /dev/null +++ b/arch/arm/mach-omap1/clock.c @@ -0,0 +1,840 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/clock.c + * + * Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * + * Modified to use omap shared clock framework by + * Tony Lindgren <tony@atomide.com> + */ +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/soc/ti/omap1-io.h> +#include <linux/spinlock.h> + +#include <asm/mach-types.h> + +#include "hardware.h" +#include "soc.h" +#include "iomap.h" +#include "clock.h" +#include "opp.h" +#include "sram.h" + +__u32 arm_idlect1_mask; +/* provide direct internal access (not via clk API) to some clocks */ +struct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; + +/* protect registeres shared among clk_enable/disable() and clk_set_rate() operations */ +static DEFINE_SPINLOCK(arm_ckctl_lock); +static DEFINE_SPINLOCK(arm_idlect2_lock); +static DEFINE_SPINLOCK(mod_conf_ctrl_0_lock); +static DEFINE_SPINLOCK(mod_conf_ctrl_1_lock); +static DEFINE_SPINLOCK(swd_clk_div_ctrl_sel_lock); + +/* + * Omap1 specific clock functions + */ + +unsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate) +{ + unsigned int val = __raw_readl(clk->enable_reg); + return val & 1 << clk->enable_bit ? 48000000 : 12000000; +} + +unsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate) +{ + u32 div = omap_readl(MOD_CONF_CTRL_1); + + div = (div >> 17) & 0x7; + div++; + + return p_rate / div; +} + +static void omap1_clk_allow_idle(struct omap1_clk *clk) +{ + struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; + + if (!(clk->flags & CLOCK_IDLE_CONTROL)) + return; + + if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count)) + arm_idlect1_mask |= 1 << iclk->idlect_shift; +} + +static void omap1_clk_deny_idle(struct omap1_clk *clk) +{ + struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; + + if (!(clk->flags & CLOCK_IDLE_CONTROL)) + return; + + if (iclk->no_idle_count++ == 0) + arm_idlect1_mask &= ~(1 << iclk->idlect_shift); +} + +static __u16 verify_ckctl_value(__u16 newval) +{ + /* This function checks for following limitations set + * by the hardware (all conditions must be true): + * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 + * ARM_CK >= TC_CK + * DSP_CK >= TC_CK + * DSPMMU_CK >= TC_CK + * + * In addition following rules are enforced: + * LCD_CK <= TC_CK + * ARMPER_CK <= TC_CK + * + * However, maximum frequencies are not checked for! + */ + __u8 per_exp; + __u8 lcd_exp; + __u8 arm_exp; + __u8 dsp_exp; + __u8 tc_exp; + __u8 dspmmu_exp; + + per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; + lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; + arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; + dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; + tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; + dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; + + if (dspmmu_exp < dsp_exp) + dspmmu_exp = dsp_exp; + if (dspmmu_exp > dsp_exp+1) + dspmmu_exp = dsp_exp+1; + if (tc_exp < arm_exp) + tc_exp = arm_exp; + if (tc_exp < dspmmu_exp) + tc_exp = dspmmu_exp; + if (tc_exp > lcd_exp) + lcd_exp = tc_exp; + if (tc_exp > per_exp) + per_exp = tc_exp; + + newval &= 0xf000; + newval |= per_exp << CKCTL_PERDIV_OFFSET; + newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; + newval |= arm_exp << CKCTL_ARMDIV_OFFSET; + newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; + newval |= tc_exp << CKCTL_TCDIV_OFFSET; + newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; + + return newval; +} + +static int calc_dsor_exp(unsigned long rate, unsigned long realrate) +{ + /* Note: If target frequency is too low, this function will return 4, + * which is invalid value. Caller must check for this value and act + * accordingly. + * + * Note: This function does not check for following limitations set + * by the hardware (all conditions must be true): + * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 + * ARM_CK >= TC_CK + * DSP_CK >= TC_CK + * DSPMMU_CK >= TC_CK + */ + unsigned dsor_exp; + + if (unlikely(realrate == 0)) + return -EIO; + + for (dsor_exp=0; dsor_exp<4; dsor_exp++) { + if (realrate <= rate) + break; + + realrate /= 2; + } + + return dsor_exp; +} + +unsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate) +{ + /* Calculate divisor encoded as 2-bit exponent */ + int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); + + /* update locally maintained rate, required by arm_ck for omap1_show_rates() */ + clk->rate = p_rate / dsor; + return clk->rate; +} + +static int omap1_clk_is_enabled(struct clk_hw *hw) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + bool api_ck_was_enabled = true; + __u32 regval32; + int ret; + + if (!clk->ops) /* no gate -- always enabled */ + return 1; + + if (clk->ops == &clkops_dspck) { + api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); + if (!api_ck_was_enabled) + if (api_ck_p->ops->enable(api_ck_p) < 0) + return 0; + } + + if (clk->flags & ENABLE_REG_32BIT) + regval32 = __raw_readl(clk->enable_reg); + else + regval32 = __raw_readw(clk->enable_reg); + + ret = regval32 & (1 << clk->enable_bit); + + if (!api_ck_was_enabled) + api_ck_p->ops->disable(api_ck_p); + + return ret; +} + + +unsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate) +{ + bool api_ck_was_enabled; + int dsor; + + /* Calculate divisor encoded as 2-bit exponent + * + * The clock control bits are in DSP domain, + * so api_ck is needed for access. + * Note that DSP_CKCTL virt addr = phys addr, so + * we must use __raw_readw() instead of omap_readw(). + */ + api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); + if (!api_ck_was_enabled) + api_ck_p->ops->enable(api_ck_p); + dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset)); + if (!api_ck_was_enabled) + api_ck_p->ops->disable(api_ck_p); + + return p_rate / dsor; +} + +/* MPU virtual clock functions */ +int omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + /* Find the highest supported frequency <= rate and switch to it */ + struct mpu_rate * ptr; + unsigned long ref_rate; + + ref_rate = ck_ref_p->rate; + + for (ptr = omap1_rate_table; ptr->rate; ptr++) { + if (!(ptr->flags & cpu_mask)) + continue; + + if (ptr->xtal != ref_rate) + continue; + + /* Can check only after xtal frequency check */ + if (ptr->rate <= rate) + break; + } + + if (!ptr->rate) + return -EINVAL; + + /* + * In most cases we should not need to reprogram DPLL. + * Reprogramming the DPLL is tricky, it must be done from SRAM. + */ + omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val); + + /* XXX Do we need to recalculate the tree below DPLL1 at this point? */ + ck_dpll1_p->rate = ptr->pll_rate; + + return 0; +} + +int omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + int dsor_exp; + u16 regval; + + dsor_exp = calc_dsor_exp(rate, p_rate); + if (dsor_exp > 3) + dsor_exp = -EINVAL; + if (dsor_exp < 0) + return dsor_exp; + + regval = __raw_readw(DSP_CKCTL); + regval &= ~(3 << clk->rate_offset); + regval |= dsor_exp << clk->rate_offset; + __raw_writew(regval, DSP_CKCTL); + clk->rate = p_rate / (1 << dsor_exp); + + return 0; +} + +long omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, + unsigned long *p_rate) +{ + int dsor_exp = calc_dsor_exp(rate, *p_rate); + + if (dsor_exp < 0) + return dsor_exp; + if (dsor_exp > 3) + dsor_exp = 3; + return *p_rate / (1 << dsor_exp); +} + +int omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + unsigned long flags; + int dsor_exp; + u16 regval; + + dsor_exp = calc_dsor_exp(rate, p_rate); + if (dsor_exp > 3) + dsor_exp = -EINVAL; + if (dsor_exp < 0) + return dsor_exp; + + /* protect ARM_CKCTL register from concurrent access via clk_enable/disable() */ + spin_lock_irqsave(&arm_ckctl_lock, flags); + + regval = omap_readw(ARM_CKCTL); + regval &= ~(3 << clk->rate_offset); + regval |= dsor_exp << clk->rate_offset; + regval = verify_ckctl_value(regval); + omap_writew(regval, ARM_CKCTL); + clk->rate = p_rate / (1 << dsor_exp); + + spin_unlock_irqrestore(&arm_ckctl_lock, flags); + + return 0; +} + +long omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) +{ + /* Find the highest supported frequency <= rate */ + struct mpu_rate * ptr; + long highest_rate; + unsigned long ref_rate; + + ref_rate = ck_ref_p->rate; + + highest_rate = -EINVAL; + + for (ptr = omap1_rate_table; ptr->rate; ptr++) { + if (!(ptr->flags & cpu_mask)) + continue; + + if (ptr->xtal != ref_rate) + continue; + + highest_rate = ptr->rate; + + /* Can check only after xtal frequency check */ + if (ptr->rate <= rate) + break; + } + + return highest_rate; +} + +static unsigned calc_ext_dsor(unsigned long rate) +{ + unsigned dsor; + + /* MCLK and BCLK divisor selection is not linear: + * freq = 96MHz / dsor + * + * RATIO_SEL range: dsor <-> RATIO_SEL + * 0..6: (RATIO_SEL+2) <-> (dsor-2) + * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) + * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 + * can not be used. + */ + for (dsor = 2; dsor < 96; ++dsor) { + if ((dsor & 1) && dsor > 8) + continue; + if (rate >= 96000000 / dsor) + break; + } + return dsor; +} + +/* XXX Only needed on 1510 */ +long omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) +{ + return rate > 24000000 ? 48000000 : 12000000; +} + +int omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + unsigned long flags; + unsigned int val; + + if (rate == 12000000) + val = 0; + else if (rate == 48000000) + val = 1 << clk->enable_bit; + else + return -EINVAL; + + /* protect MOD_CONF_CTRL_0 register from concurrent access via clk_enable/disable() */ + spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); + + val |= __raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit); + __raw_writel(val, clk->enable_reg); + + spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); + + clk->rate = rate; + + return 0; +} + +/* External clock (MCLK & BCLK) functions */ +int omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + unsigned long flags; + unsigned dsor; + __u16 ratio_bits; + + dsor = calc_ext_dsor(rate); + clk->rate = 96000000 / dsor; + if (dsor > 8) + ratio_bits = ((dsor - 8) / 2 + 6) << 2; + else + ratio_bits = (dsor - 2) << 2; + + /* protect SWD_CLK_DIV_CTRL_SEL register from concurrent access via clk_enable/disable() */ + spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); + + ratio_bits |= __raw_readw(clk->enable_reg) & ~0xfd; + __raw_writew(ratio_bits, clk->enable_reg); + + spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); + + return 0; +} + +static int calc_div_sossi(unsigned long rate, unsigned long p_rate) +{ + int div; + + /* Round towards slower frequency */ + div = (p_rate + rate - 1) / rate; + + return --div; +} + +long omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) +{ + int div; + + div = calc_div_sossi(rate, *p_rate); + if (div < 0) + div = 0; + else if (div > 7) + div = 7; + + return *p_rate / (div + 1); +} + +int omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) +{ + unsigned long flags; + u32 l; + int div; + + div = calc_div_sossi(rate, p_rate); + if (div < 0 || div > 7) + return -EINVAL; + + /* protect MOD_CONF_CTRL_1 register from concurrent access via clk_enable/disable() */ + spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); + + l = omap_readl(MOD_CONF_CTRL_1); + l &= ~(7 << 17); + l |= div << 17; + omap_writel(l, MOD_CONF_CTRL_1); + + clk->rate = p_rate / (div + 1); + + spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); + + return 0; +} + +long omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) +{ + return 96000000 / calc_ext_dsor(rate); +} + +int omap1_init_ext_clk(struct omap1_clk *clk) +{ + unsigned dsor; + __u16 ratio_bits; + + /* Determine current rate and ensure clock is based on 96MHz APLL */ + ratio_bits = __raw_readw(clk->enable_reg) & ~1; + __raw_writew(ratio_bits, clk->enable_reg); + + ratio_bits = (ratio_bits & 0xfc) >> 2; + if (ratio_bits > 6) + dsor = (ratio_bits - 6) * 2 + 8; + else + dsor = ratio_bits + 2; + + clk-> rate = 96000000 / dsor; + + return 0; +} + +static int omap1_clk_enable(struct clk_hw *hw) +{ + struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw)); + int ret = 0; + + if (parent && clk->flags & CLOCK_NO_IDLE_PARENT) + omap1_clk_deny_idle(parent); + + if (clk->ops && !(WARN_ON(!clk->ops->enable))) + ret = clk->ops->enable(clk); + + return ret; +} + +static void omap1_clk_disable(struct clk_hw *hw) +{ + struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw)); + + if (clk->ops && !(WARN_ON(!clk->ops->disable))) + clk->ops->disable(clk); + + if (likely(parent) && clk->flags & CLOCK_NO_IDLE_PARENT) + omap1_clk_allow_idle(parent); +} + +static int omap1_clk_enable_generic(struct omap1_clk *clk) +{ + unsigned long flags; + __u16 regval16; + __u32 regval32; + + if (unlikely(clk->enable_reg == NULL)) { + printk(KERN_ERR "clock.c: Enable for %s without enable code\n", + clk_hw_get_name(&clk->hw)); + return -EINVAL; + } + + /* protect clk->enable_reg from concurrent access via clk_set_rate() */ + if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) + spin_lock_irqsave(&arm_ckctl_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) + spin_lock_irqsave(&arm_idlect2_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) + spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) + spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) + spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); + + if (clk->flags & ENABLE_REG_32BIT) { + regval32 = __raw_readl(clk->enable_reg); + regval32 |= (1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + } else { + regval16 = __raw_readw(clk->enable_reg); + regval16 |= (1 << clk->enable_bit); + __raw_writew(regval16, clk->enable_reg); + } + + if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) + spin_unlock_irqrestore(&arm_ckctl_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) + spin_unlock_irqrestore(&arm_idlect2_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) + spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) + spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) + spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); + + return 0; +} + +static void omap1_clk_disable_generic(struct omap1_clk *clk) +{ + unsigned long flags; + __u16 regval16; + __u32 regval32; + + if (clk->enable_reg == NULL) + return; + + /* protect clk->enable_reg from concurrent access via clk_set_rate() */ + if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) + spin_lock_irqsave(&arm_ckctl_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) + spin_lock_irqsave(&arm_idlect2_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) + spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) + spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) + spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); + + if (clk->flags & ENABLE_REG_32BIT) { + regval32 = __raw_readl(clk->enable_reg); + regval32 &= ~(1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + } else { + regval16 = __raw_readw(clk->enable_reg); + regval16 &= ~(1 << clk->enable_bit); + __raw_writew(regval16, clk->enable_reg); + } + + if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) + spin_unlock_irqrestore(&arm_ckctl_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) + spin_unlock_irqrestore(&arm_idlect2_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) + spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) + spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); + else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) + spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); +} + +const struct clkops clkops_generic = { + .enable = omap1_clk_enable_generic, + .disable = omap1_clk_disable_generic, +}; + +static int omap1_clk_enable_dsp_domain(struct omap1_clk *clk) +{ + bool api_ck_was_enabled; + int retval = 0; + + api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); + if (!api_ck_was_enabled) + retval = api_ck_p->ops->enable(api_ck_p); + + if (!retval) { + retval = omap1_clk_enable_generic(clk); + + if (!api_ck_was_enabled) + api_ck_p->ops->disable(api_ck_p); + } + + return retval; +} + +static void omap1_clk_disable_dsp_domain(struct omap1_clk *clk) +{ + bool api_ck_was_enabled; + + api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); + if (!api_ck_was_enabled) + if (api_ck_p->ops->enable(api_ck_p) < 0) + return; + + omap1_clk_disable_generic(clk); + + if (!api_ck_was_enabled) + api_ck_p->ops->disable(api_ck_p); +} + +const struct clkops clkops_dspck = { + .enable = omap1_clk_enable_dsp_domain, + .disable = omap1_clk_disable_dsp_domain, +}; + +/* XXX SYSC register handling does not belong in the clock framework */ +static int omap1_clk_enable_uart_functional_16xx(struct omap1_clk *clk) +{ + int ret; + struct uart_clk *uclk; + + ret = omap1_clk_enable_generic(clk); + if (ret == 0) { + /* Set smart idle acknowledgement mode */ + uclk = (struct uart_clk *)clk; + omap_writeb((omap_readb(uclk->sysc_addr) & ~0x10) | 8, + uclk->sysc_addr); + } + + return ret; +} + +/* XXX SYSC register handling does not belong in the clock framework */ +static void omap1_clk_disable_uart_functional_16xx(struct omap1_clk *clk) +{ + struct uart_clk *uclk; + + /* Set force idle acknowledgement mode */ + uclk = (struct uart_clk *)clk; + omap_writeb((omap_readb(uclk->sysc_addr) & ~0x18), uclk->sysc_addr); + + omap1_clk_disable_generic(clk); +} + +/* XXX SYSC register handling does not belong in the clock framework */ +const struct clkops clkops_uart_16xx = { + .enable = omap1_clk_enable_uart_functional_16xx, + .disable = omap1_clk_disable_uart_functional_16xx, +}; + +static unsigned long omap1_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + + if (clk->recalc) + return clk->recalc(clk, p_rate); + + return clk->rate; +} + +static long omap1_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *p_rate) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + + if (clk->round_rate != NULL) + return clk->round_rate(clk, rate, p_rate); + + return omap1_clk_recalc_rate(hw, *p_rate); +} + +static int omap1_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + int ret = -EINVAL; + + if (clk->set_rate) + ret = clk->set_rate(clk, rate, p_rate); + return ret; +} + +/* + * Omap1 clock reset and init functions + */ + +static int omap1_clk_init_op(struct clk_hw *hw) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + + if (clk->init) + return clk->init(clk); + + return 0; +} + +#ifdef CONFIG_OMAP_RESET_CLOCKS + +static void omap1_clk_disable_unused(struct clk_hw *hw) +{ + struct omap1_clk *clk = to_omap1_clk(hw); + const char *name = clk_hw_get_name(hw); + + /* Clocks in the DSP domain need api_ck. Just assume bootloader + * has not enabled any DSP clocks */ + if (clk->enable_reg == DSP_IDLECT2) { + pr_info("Skipping reset check for DSP domain clock \"%s\"\n", name); + return; + } + + pr_info("Disabling unused clock \"%s\"... ", name); + omap1_clk_disable(hw); + printk(" done\n"); +} + +#endif + +const struct clk_ops omap1_clk_gate_ops = { + .enable = omap1_clk_enable, + .disable = omap1_clk_disable, + .is_enabled = omap1_clk_is_enabled, +#ifdef CONFIG_OMAP_RESET_CLOCKS + .disable_unused = omap1_clk_disable_unused, +#endif +}; + +const struct clk_ops omap1_clk_rate_ops = { + .recalc_rate = omap1_clk_recalc_rate, + .round_rate = omap1_clk_round_rate, + .set_rate = omap1_clk_set_rate, + .init = omap1_clk_init_op, +}; + +const struct clk_ops omap1_clk_full_ops = { + .enable = omap1_clk_enable, + .disable = omap1_clk_disable, + .is_enabled = omap1_clk_is_enabled, +#ifdef CONFIG_OMAP_RESET_CLOCKS + .disable_unused = omap1_clk_disable_unused, +#endif + .recalc_rate = omap1_clk_recalc_rate, + .round_rate = omap1_clk_round_rate, + .set_rate = omap1_clk_set_rate, + .init = omap1_clk_init_op, +}; + +/* + * OMAP specific clock functions shared between omap1 and omap2 + */ + +/* Used for clocks that always have same value as the parent clock */ +unsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate) +{ + return p_rate; +} + +/* + * Used for clocks that have the same value as the parent clock, + * divided by some factor + */ +unsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate) +{ + WARN_ON(!clk->fixed_div); + + return p_rate / clk->fixed_div; +} + +/* Propagate rate to children */ +void propagate_rate(struct omap1_clk *tclk) +{ + struct clk *clkp; + + /* depend on CCF ability to recalculate new rates across whole clock subtree */ + if (WARN_ON(!(clk_hw_get_flags(&tclk->hw) & CLK_GET_RATE_NOCACHE))) + return; + + clkp = clk_get_sys(NULL, clk_hw_get_name(&tclk->hw)); + if (WARN_ON(!clkp)) + return; + + clk_get_rate(clkp); + clk_put(clkp); +} + +const struct clk_ops omap1_clk_null_ops = { +}; + +/* + * Dummy clock + * + * Used for clock aliases that are needed on some OMAPs, but not others + */ +struct omap1_clk dummy_ck __refdata = { + .hw.init = CLK_HW_INIT_NO_PARENT("dummy", &omap1_clk_null_ops, 0), +}; diff --git a/arch/arm/mach-omap1/clock.h b/arch/arm/mach-omap1/clock.h new file mode 100644 index 000000000..16cfb2e86 --- /dev/null +++ b/arch/arm/mach-omap1/clock.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/mach-omap1/clock.h + * + * Copyright (C) 2004 - 2005, 2009 Nokia corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_CLOCK_H +#define __ARCH_ARM_MACH_OMAP1_CLOCK_H + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> + +struct module; +struct omap1_clk; + +struct omap_clk { + u16 cpu; + struct clk_lookup lk; +}; + +#define CLK(dev, con, ck, cp) \ + { \ + .cpu = cp, \ + .lk = { \ + .dev_id = dev, \ + .con_id = con, \ + .clk_hw = ck, \ + }, \ + } + +/* Platform flags for the clkdev-OMAP integration code */ +#define CK_310 (1 << 0) +#define CK_7XX (1 << 1) /* 7xx, 850 */ +#define CK_1510 (1 << 2) +#define CK_16XX (1 << 3) /* 16xx, 17xx, 5912 */ +#define CK_1710 (1 << 4) /* 1710 extra for rate selection */ + +/** + * struct clkops - some clock function pointers + * @enable: fn ptr that enables the current clock in hardware + * @disable: fn ptr that enables the current clock in hardware + * @allow_idle: fn ptr that enables autoidle for the current clock in hardware + */ +struct clkops { + int (*enable)(struct omap1_clk *clk); + void (*disable)(struct omap1_clk *clk); +}; + +/* + * struct clk.flags possibilities + * + * XXX document the rest of the clock flags here + */ +#define ENABLE_REG_32BIT (1 << 0) /* Use 32-bit access */ +#define CLOCK_IDLE_CONTROL (1 << 1) +#define CLOCK_NO_IDLE_PARENT (1 << 2) + +/** + * struct omap1_clk - OMAP1 struct clk + * @hw: struct clk_hw for common clock framework integration + * @ops: struct clkops * for this clock + * @rate: current clock rate + * @enable_reg: register to write to enable the clock (see @enable_bit) + * @recalc: fn ptr that returns the clock's current rate + * @set_rate: fn ptr that can change the clock's current rate + * @round_rate: fn ptr that can round the clock's current rate + * @init: fn ptr to do clock-specific initialization + * @enable_bit: bitshift to write to enable/disable the clock (see @enable_reg) + * @fixed_div: when > 0, this clock's rate is its parent's rate / @fixed_div + * @flags: see "struct clk.flags possibilities" above + * @rate_offset: bitshift for rate selection bitfield (OMAP1 only) + */ +struct omap1_clk { + struct clk_hw hw; + const struct clkops *ops; + unsigned long rate; + void __iomem *enable_reg; + unsigned long (*recalc)(struct omap1_clk *clk, unsigned long rate); + int (*set_rate)(struct omap1_clk *clk, unsigned long rate, + unsigned long p_rate); + long (*round_rate)(struct omap1_clk *clk, unsigned long rate, + unsigned long *p_rate); + int (*init)(struct omap1_clk *clk); + u8 enable_bit; + u8 fixed_div; + u8 flags; + u8 rate_offset; +}; +#define to_omap1_clk(_hw) container_of(_hw, struct omap1_clk, hw) + +void propagate_rate(struct omap1_clk *clk); +unsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate); +unsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate); + +extern struct omap1_clk dummy_ck; + +int omap1_clk_init(void); +void omap1_clk_late_init(void); +unsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate); +long omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate); +int omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate); +unsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate); +unsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate); +int omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate, + unsigned long p_rate); +long omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate); +int omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate); +unsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate); +int omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate); +long omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate); +int omap1_init_ext_clk(struct omap1_clk *clk); +int omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate); +long omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate); +int omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate); +long omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, + unsigned long *p_rate); + +struct uart_clk { + struct omap1_clk clk; + unsigned long sysc_addr; +}; + +/* Provide a method for preventing idling some ARM IDLECT clocks */ +struct arm_idlect1_clk { + struct omap1_clk clk; + unsigned long no_idle_count; + __u8 idlect_shift; +}; + +/* ARM_CKCTL bit shifts */ +#define CKCTL_PERDIV_OFFSET 0 +#define CKCTL_LCDDIV_OFFSET 2 +#define CKCTL_ARMDIV_OFFSET 4 +#define CKCTL_DSPDIV_OFFSET 6 +#define CKCTL_TCDIV_OFFSET 8 +#define CKCTL_DSPMMUDIV_OFFSET 10 +/*#define ARM_TIMXO 12*/ +#define EN_DSPCK 13 +/*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */ +/* DSP_CKCTL bit shifts */ +#define CKCTL_DSPPERDIV_OFFSET 0 + +/* ARM_IDLECT2 bit shifts */ +#define EN_WDTCK 0 +#define EN_XORPCK 1 +#define EN_PERCK 2 +#define EN_LCDCK 3 +#define EN_LBCK 4 /* Not on 1610/1710 */ +/*#define EN_HSABCK 5*/ +#define EN_APICK 6 +#define EN_TIMCK 7 +#define DMACK_REQ 8 +#define EN_GPIOCK 9 /* Not on 1610/1710 */ +/*#define EN_LBFREECK 10*/ +#define EN_CKOUT_ARM 11 + +/* ARM_IDLECT3 bit shifts */ +#define EN_OCPI_CK 0 +#define EN_TC1_CK 2 +#define EN_TC2_CK 4 + +/* DSP_IDLECT2 bit shifts (0,1,2 are same as for ARM_IDLECT2) */ +#define EN_DSPTIMCK 5 + +/* Various register defines for clock controls scattered around OMAP chip */ +#define SDW_MCLK_INV_BIT 2 /* In ULPD_CLKC_CTRL */ +#define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */ +#define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */ +#define SWD_ULPD_PLL_CLK_REQ 1 /* In SWD_CLK_DIV_CTRL_SEL */ +#define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */ +#define SWD_CLK_DIV_CTRL_SEL 0xfffe0874 +#define COM_CLK_DIV_CTRL_SEL 0xfffe0878 +#define SOFT_REQ_REG 0xfffe0834 +#define SOFT_REQ_REG2 0xfffe0880 + +extern __u32 arm_idlect1_mask; +extern struct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; + +extern const struct clkops clkops_dspck; +extern const struct clkops clkops_uart_16xx; +extern const struct clkops clkops_generic; + +/* used for passing SoC type to omap1_{select,round_to}_table_rate() */ +extern u32 cpu_mask; + +extern const struct clk_ops omap1_clk_null_ops; +extern const struct clk_ops omap1_clk_gate_ops; +extern const struct clk_ops omap1_clk_rate_ops; +extern const struct clk_ops omap1_clk_full_ops; + +#endif diff --git a/arch/arm/mach-omap1/clock_data.c b/arch/arm/mach-omap1/clock_data.c new file mode 100644 index 000000000..96d846c37 --- /dev/null +++ b/arch/arm/mach-omap1/clock_data.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/clock_data.c + * + * Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc + * + * To do: + * - Clocks that are only available on some chips should be marked with the + * chips that they are present on. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/mach-types.h> /* for machine_is_* */ + +#include "soc.h" +#include "hardware.h" +#include "usb.h" /* for OTG_BASE */ +#include "iomap.h" +#include "clock.h" +#include "sram.h" + +/* Some ARM_IDLECT1 bit shifts - used in struct arm_idlect1_clk */ +#define IDL_CLKOUT_ARM_SHIFT 12 +#define IDLTIM_ARM_SHIFT 9 +#define IDLAPI_ARM_SHIFT 8 +#define IDLIF_ARM_SHIFT 6 +#define IDLLB_ARM_SHIFT 4 /* undocumented? */ +#define OMAP1510_IDLLCD_ARM_SHIFT 3 /* undocumented? */ +#define IDLPER_ARM_SHIFT 2 +#define IDLXORP_ARM_SHIFT 1 +#define IDLWDT_ARM_SHIFT 0 + +/* Some MOD_CONF_CTRL_0 bit shifts - used in struct clk.enable_bit */ +#define CONF_MOD_UART3_CLK_MODE_R 31 +#define CONF_MOD_UART2_CLK_MODE_R 30 +#define CONF_MOD_UART1_CLK_MODE_R 29 +#define CONF_MOD_MMC_SD_CLK_REQ_R 23 +#define CONF_MOD_MCBSP3_AUXON 20 + +/* Some MOD_CONF_CTRL_1 bit shifts - used in struct clk.enable_bit */ +#define CONF_MOD_SOSSI_CLK_EN_R 16 + +/* Some OTG_SYSCON_2-specific bit fields */ +#define OTG_SYSCON_2_UHOST_EN_SHIFT 8 + +/* Some SOFT_REQ_REG bit fields - used in struct clk.enable_bit */ +#define SOFT_MMC2_DPLL_REQ_SHIFT 13 +#define SOFT_MMC_DPLL_REQ_SHIFT 12 +#define SOFT_UART3_DPLL_REQ_SHIFT 11 +#define SOFT_UART2_DPLL_REQ_SHIFT 10 +#define SOFT_UART1_DPLL_REQ_SHIFT 9 +#define SOFT_USB_OTG_DPLL_REQ_SHIFT 8 +#define SOFT_CAM_DPLL_REQ_SHIFT 7 +#define SOFT_COM_MCKO_REQ_SHIFT 6 +#define SOFT_PERIPH_REQ_SHIFT 5 /* sys_ck gate for UART2 ? */ +#define USB_REQ_EN_SHIFT 4 +#define SOFT_USB_REQ_SHIFT 3 /* sys_ck gate for USB host? */ +#define SOFT_SDW_REQ_SHIFT 2 /* sys_ck gate for Bluetooth? */ +#define SOFT_COM_REQ_SHIFT 1 /* sys_ck gate for com proc? */ +#define SOFT_DPLL_REQ_SHIFT 0 + +/* + * Omap1 clocks + */ + +static struct omap1_clk ck_ref = { + .hw.init = CLK_HW_INIT_NO_PARENT("ck_ref", &omap1_clk_rate_ops, 0), + .rate = 12000000, +}; + +static struct omap1_clk ck_dpll1 = { + .hw.init = CLK_HW_INIT("ck_dpll1", "ck_ref", &omap1_clk_rate_ops, + /* + * force recursive refresh of rates of the clock + * and its children when clk_get_rate() is called + */ + CLK_GET_RATE_NOCACHE), +}; + +/* + * FIXME: This clock seems to be necessary but no-one has asked for its + * activation. [ FIX: SoSSI, SSR ] + */ +static struct arm_idlect1_clk ck_dpll1out = { + .clk = { + .hw.init = CLK_HW_INIT("ck_dpll1out", "ck_dpll1", &omap1_clk_gate_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL | ENABLE_REG_32BIT, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_CKOUT_ARM, + }, + .idlect_shift = IDL_CLKOUT_ARM_SHIFT, +}; + +static struct omap1_clk sossi_ck = { + .hw.init = CLK_HW_INIT("ck_sossi", "ck_dpll1out", &omap1_clk_full_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_NO_IDLE_PARENT | ENABLE_REG_32BIT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1), + .enable_bit = CONF_MOD_SOSSI_CLK_EN_R, + .recalc = &omap1_sossi_recalc, + .round_rate = &omap1_round_sossi_rate, + .set_rate = &omap1_set_sossi_rate, +}; + +static struct omap1_clk arm_ck = { + .hw.init = CLK_HW_INIT("arm_ck", "ck_dpll1", &omap1_clk_rate_ops, 0), + .rate_offset = CKCTL_ARMDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, +}; + +static struct arm_idlect1_clk armper_ck = { + .clk = { + .hw.init = CLK_HW_INIT("armper_ck", "ck_dpll1", &omap1_clk_full_ops, + CLK_IS_CRITICAL), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_PERCK, + .rate_offset = CKCTL_PERDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, + }, + .idlect_shift = IDLPER_ARM_SHIFT, +}; + +/* + * FIXME: This clock seems to be necessary but no-one has asked for its + * activation. [ GPIO code for 1510 ] + */ +static struct omap1_clk arm_gpio_ck = { + .hw.init = CLK_HW_INIT("ick", "ck_dpll1", &omap1_clk_gate_ops, CLK_IS_CRITICAL), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_GPIOCK, +}; + +static struct arm_idlect1_clk armxor_ck = { + .clk = { + .hw.init = CLK_HW_INIT("armxor_ck", "ck_ref", &omap1_clk_gate_ops, + CLK_IS_CRITICAL), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_XORPCK, + }, + .idlect_shift = IDLXORP_ARM_SHIFT, +}; + +static struct arm_idlect1_clk armtim_ck = { + .clk = { + .hw.init = CLK_HW_INIT("armtim_ck", "ck_ref", &omap1_clk_gate_ops, + CLK_IS_CRITICAL), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_TIMCK, + }, + .idlect_shift = IDLTIM_ARM_SHIFT, +}; + +static struct arm_idlect1_clk armwdt_ck = { + .clk = { + .hw.init = CLK_HW_INIT("armwdt_ck", "ck_ref", &omap1_clk_full_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_WDTCK, + .fixed_div = 14, + .recalc = &omap_fixed_divisor_recalc, + }, + .idlect_shift = IDLWDT_ARM_SHIFT, +}; + +static struct omap1_clk arminth_ck16xx = { + .hw.init = CLK_HW_INIT("arminth_ck", "arm_ck", &omap1_clk_null_ops, 0), + /* Note: On 16xx the frequency can be divided by 2 by programming + * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 + * + * 1510 version is in TC clocks. + */ +}; + +static struct omap1_clk dsp_ck = { + .hw.init = CLK_HW_INIT("dsp_ck", "ck_dpll1", &omap1_clk_full_ops, 0), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_CKCTL), + .enable_bit = EN_DSPCK, + .rate_offset = CKCTL_DSPDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, +}; + +static struct omap1_clk dspmmu_ck = { + .hw.init = CLK_HW_INIT("dspmmu_ck", "ck_dpll1", &omap1_clk_rate_ops, 0), + .rate_offset = CKCTL_DSPMMUDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, +}; + +static struct omap1_clk dspper_ck = { + .hw.init = CLK_HW_INIT("dspper_ck", "ck_dpll1", &omap1_clk_full_ops, 0), + .ops = &clkops_dspck, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_PERCK, + .rate_offset = CKCTL_PERDIV_OFFSET, + .recalc = &omap1_ckctl_recalc_dsp_domain, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = &omap1_clk_set_rate_dsp_domain, +}; + +static struct omap1_clk dspxor_ck = { + .hw.init = CLK_HW_INIT("dspxor_ck", "ck_ref", &omap1_clk_gate_ops, 0), + .ops = &clkops_dspck, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_XORPCK, +}; + +static struct omap1_clk dsptim_ck = { + .hw.init = CLK_HW_INIT("dsptim_ck", "ck_ref", &omap1_clk_gate_ops, 0), + .ops = &clkops_dspck, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_DSPTIMCK, +}; + +static struct arm_idlect1_clk tc_ck = { + .clk = { + .hw.init = CLK_HW_INIT("tc_ck", "ck_dpll1", &omap1_clk_rate_ops, 0), + .flags = CLOCK_IDLE_CONTROL, + .rate_offset = CKCTL_TCDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, + }, + .idlect_shift = IDLIF_ARM_SHIFT, +}; + +static struct omap1_clk arminth_ck1510 = { + .hw.init = CLK_HW_INIT("arminth_ck", "tc_ck", &omap1_clk_null_ops, 0), + /* Note: On 1510 the frequency follows TC_CK + * + * 16xx version is in MPU clocks. + */ +}; + +static struct omap1_clk tipb_ck = { + /* No-idle controlled by "tc_ck" */ + .hw.init = CLK_HW_INIT("tipb_ck", "tc_ck", &omap1_clk_null_ops, 0), +}; + +static struct omap1_clk l3_ocpi_ck = { + /* No-idle controlled by "tc_ck" */ + .hw.init = CLK_HW_INIT("l3_ocpi_ck", "tc_ck", &omap1_clk_gate_ops, 0), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT3), + .enable_bit = EN_OCPI_CK, +}; + +static struct omap1_clk tc1_ck = { + .hw.init = CLK_HW_INIT("tc1_ck", "tc_ck", &omap1_clk_gate_ops, 0), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT3), + .enable_bit = EN_TC1_CK, +}; + +/* + * FIXME: This clock seems to be necessary but no-one has asked for its + * activation. [ pm.c (SRAM), CCP, Camera ] + */ + +static struct omap1_clk tc2_ck = { + .hw.init = CLK_HW_INIT("tc2_ck", "tc_ck", &omap1_clk_gate_ops, CLK_IS_CRITICAL), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT3), + .enable_bit = EN_TC2_CK, +}; + +static struct omap1_clk dma_ck = { + /* No-idle controlled by "tc_ck" */ + .hw.init = CLK_HW_INIT("dma_ck", "tc_ck", &omap1_clk_null_ops, 0), +}; + +static struct omap1_clk dma_lcdfree_ck = { + .hw.init = CLK_HW_INIT("dma_lcdfree_ck", "tc_ck", &omap1_clk_null_ops, 0), +}; + +static struct arm_idlect1_clk api_ck = { + .clk = { + .hw.init = CLK_HW_INIT("api_ck", "tc_ck", &omap1_clk_gate_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_APICK, + }, + .idlect_shift = IDLAPI_ARM_SHIFT, +}; + +static struct arm_idlect1_clk lb_ck = { + .clk = { + .hw.init = CLK_HW_INIT("lb_ck", "tc_ck", &omap1_clk_gate_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_LBCK, + }, + .idlect_shift = IDLLB_ARM_SHIFT, +}; + +static struct omap1_clk rhea1_ck = { + .hw.init = CLK_HW_INIT("rhea1_ck", "tc_ck", &omap1_clk_null_ops, 0), +}; + +static struct omap1_clk rhea2_ck = { + .hw.init = CLK_HW_INIT("rhea2_ck", "tc_ck", &omap1_clk_null_ops, 0), +}; + +static struct omap1_clk lcd_ck_16xx = { + .hw.init = CLK_HW_INIT("lcd_ck", "ck_dpll1", &omap1_clk_full_ops, 0), + .ops = &clkops_generic, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_LCDCK, + .rate_offset = CKCTL_LCDDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, +}; + +static struct arm_idlect1_clk lcd_ck_1510 = { + .clk = { + .hw.init = CLK_HW_INIT("lcd_ck", "ck_dpll1", &omap1_clk_full_ops, 0), + .ops = &clkops_generic, + .flags = CLOCK_IDLE_CONTROL, + .enable_reg = OMAP1_IO_ADDRESS(ARM_IDLECT2), + .enable_bit = EN_LCDCK, + .rate_offset = CKCTL_LCDDIV_OFFSET, + .recalc = &omap1_ckctl_recalc, + .round_rate = omap1_clk_round_rate_ckctl_arm, + .set_rate = omap1_clk_set_rate_ckctl_arm, + }, + .idlect_shift = OMAP1510_IDLLCD_ARM_SHIFT, +}; + + +/* + * XXX The enable_bit here is misused - it simply switches between 12MHz + * and 48MHz. Reimplement with clk_mux. + * + * XXX does this need SYSC register handling? + */ +static struct omap1_clk uart1_1510 = { + /* Direct from ULPD, no real parent */ + .hw.init = CLK_HW_INIT("uart1_ck", "armper_ck", &omap1_clk_full_ops, 0), + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_UART1_CLK_MODE_R, + .round_rate = &omap1_round_uart_rate, + .set_rate = &omap1_set_uart_rate, + .recalc = &omap1_uart_recalc, +}; + +/* + * XXX The enable_bit here is misused - it simply switches between 12MHz + * and 48MHz. Reimplement with clk_mux. + * + * XXX SYSC register handling does not belong in the clock framework + */ +static struct uart_clk uart1_16xx = { + .clk = { + .ops = &clkops_uart_16xx, + /* Direct from ULPD, no real parent */ + .hw.init = CLK_HW_INIT("uart1_ck", "armper_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_UART1_CLK_MODE_R, + }, + .sysc_addr = 0xfffb0054, +}; + +/* + * XXX The enable_bit here is misused - it simply switches between 12MHz + * and 48MHz. Reimplement with clk_mux. + * + * XXX does this need SYSC register handling? + */ +static struct omap1_clk uart2_ck = { + /* Direct from ULPD, no real parent */ + .hw.init = CLK_HW_INIT("uart2_ck", "armper_ck", &omap1_clk_full_ops, 0), + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_UART2_CLK_MODE_R, + .round_rate = &omap1_round_uart_rate, + .set_rate = &omap1_set_uart_rate, + .recalc = &omap1_uart_recalc, +}; + +/* + * XXX The enable_bit here is misused - it simply switches between 12MHz + * and 48MHz. Reimplement with clk_mux. + * + * XXX does this need SYSC register handling? + */ +static struct omap1_clk uart3_1510 = { + /* Direct from ULPD, no real parent */ + .hw.init = CLK_HW_INIT("uart3_ck", "armper_ck", &omap1_clk_full_ops, 0), + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_UART3_CLK_MODE_R, + .round_rate = &omap1_round_uart_rate, + .set_rate = &omap1_set_uart_rate, + .recalc = &omap1_uart_recalc, +}; + +/* + * XXX The enable_bit here is misused - it simply switches between 12MHz + * and 48MHz. Reimplement with clk_mux. + * + * XXX SYSC register handling does not belong in the clock framework + */ +static struct uart_clk uart3_16xx = { + .clk = { + .ops = &clkops_uart_16xx, + /* Direct from ULPD, no real parent */ + .hw.init = CLK_HW_INIT("uart3_ck", "armper_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_UART3_CLK_MODE_R, + }, + .sysc_addr = 0xfffb9854, +}; + +static struct omap1_clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("usb_clko", &omap1_clk_full_ops, 0), + .rate = 6000000, + .flags = ENABLE_REG_32BIT, + .enable_reg = OMAP1_IO_ADDRESS(ULPD_CLOCK_CTRL), + .enable_bit = USB_MCLK_EN_BIT, +}; + +static struct omap1_clk usb_hhc_ck1510 = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("usb_hhc_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ + .flags = ENABLE_REG_32BIT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = USB_HOST_HHC_UHOST_EN, +}; + +static struct omap1_clk usb_hhc_ck16xx = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("usb_hhc_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ + .flags = ENABLE_REG_32BIT, + .enable_reg = OMAP1_IO_ADDRESS(OTG_BASE + 0x08), /* OTG_SYSCON_2 */ + .enable_bit = OTG_SYSCON_2_UHOST_EN_SHIFT +}; + +static struct omap1_clk usb_dc_ck = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("usb_dc_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .enable_reg = OMAP1_IO_ADDRESS(SOFT_REQ_REG), + .enable_bit = SOFT_USB_OTG_DPLL_REQ_SHIFT, +}; + +static struct omap1_clk uart1_7xx = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("uart1_ck", &omap1_clk_full_ops, 0), + .rate = 12000000, + .enable_reg = OMAP1_IO_ADDRESS(SOFT_REQ_REG), + .enable_bit = 9, +}; + +static struct omap1_clk uart2_7xx = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent */ + .hw.init = CLK_HW_INIT_NO_PARENT("uart2_ck", &omap1_clk_full_ops, 0), + .rate = 12000000, + .enable_reg = OMAP1_IO_ADDRESS(SOFT_REQ_REG), + .enable_bit = 11, +}; + +static struct omap1_clk mclk_1510 = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .hw.init = CLK_HW_INIT_NO_PARENT("mclk", &omap1_clk_full_ops, 0), + .rate = 12000000, + .enable_reg = OMAP1_IO_ADDRESS(SOFT_REQ_REG), + .enable_bit = SOFT_COM_MCKO_REQ_SHIFT, +}; + +static struct omap1_clk mclk_16xx = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .hw.init = CLK_HW_INIT_NO_PARENT("mclk", &omap1_clk_full_ops, 0), + .enable_reg = OMAP1_IO_ADDRESS(COM_CLK_DIV_CTRL_SEL), + .enable_bit = COM_ULPD_PLL_CLK_REQ, + .set_rate = &omap1_set_ext_clk_rate, + .round_rate = &omap1_round_ext_clk_rate, + .init = &omap1_init_ext_clk, +}; + +static struct omap1_clk bclk_1510 = { + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .hw.init = CLK_HW_INIT_NO_PARENT("bclk", &omap1_clk_rate_ops, 0), + .rate = 12000000, +}; + +static struct omap1_clk bclk_16xx = { + .ops = &clkops_generic, + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .hw.init = CLK_HW_INIT_NO_PARENT("bclk", &omap1_clk_full_ops, 0), + .enable_reg = OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL), + .enable_bit = SWD_ULPD_PLL_CLK_REQ, + .set_rate = &omap1_set_ext_clk_rate, + .round_rate = &omap1_round_ext_clk_rate, + .init = &omap1_init_ext_clk, +}; + +static struct omap1_clk mmc1_ck = { + .ops = &clkops_generic, + /* Functional clock is direct from ULPD, interface clock is ARMPER */ + .hw.init = CLK_HW_INIT("mmc1_ck", "armper_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = CONF_MOD_MMC_SD_CLK_REQ_R, +}; + +/* + * XXX MOD_CONF_CTRL_0 bit 20 is defined in the 1510 TRM as + * CONF_MOD_MCBSP3_AUXON ?? + */ +static struct omap1_clk mmc2_ck = { + .ops = &clkops_generic, + /* Functional clock is direct from ULPD, interface clock is ARMPER */ + .hw.init = CLK_HW_INIT("mmc2_ck", "armper_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0), + .enable_bit = 20, +}; + +static struct omap1_clk mmc3_ck = { + .ops = &clkops_generic, + /* Functional clock is direct from ULPD, interface clock is ARMPER */ + .hw.init = CLK_HW_INIT("mmc3_ck", "armper_ck", &omap1_clk_full_ops, 0), + .rate = 48000000, + .flags = ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + .enable_reg = OMAP1_IO_ADDRESS(SOFT_REQ_REG), + .enable_bit = SOFT_MMC_DPLL_REQ_SHIFT, +}; + +static struct omap1_clk virtual_ck_mpu = { + /* Is smarter alias for arm_ck */ + .hw.init = CLK_HW_INIT("mpu", "arm_ck", &omap1_clk_rate_ops, 0), + .recalc = &followparent_recalc, + .set_rate = &omap1_select_table_rate, + .round_rate = &omap1_round_to_table_rate, +}; + +/* virtual functional clock domain for I2C. Just for making sure that ARMXOR_CK +remains active during MPU idle whenever this is enabled */ +static struct omap1_clk i2c_fck = { + .hw.init = CLK_HW_INIT("i2c_fck", "armxor_ck", &omap1_clk_gate_ops, 0), + .flags = CLOCK_NO_IDLE_PARENT, +}; + +static struct omap1_clk i2c_ick = { + .hw.init = CLK_HW_INIT("i2c_ick", "armper_ck", &omap1_clk_gate_ops, 0), + .flags = CLOCK_NO_IDLE_PARENT, +}; + +/* + * clkdev integration + */ + +static struct omap_clk omap_clks[] = { + /* non-ULPD clocks */ + CLK(NULL, "ck_ref", &ck_ref.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK(NULL, "ck_dpll1", &ck_dpll1.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + /* CK_GEN1 clocks */ + CLK(NULL, "ck_dpll1out", &ck_dpll1out.clk.hw, CK_16XX), + CLK(NULL, "ck_sossi", &sossi_ck.hw, CK_16XX), + CLK(NULL, "arm_ck", &arm_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "armper_ck", &armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap_gpio.0", "ick", &arm_gpio_ck.hw, CK_1510 | CK_310), + CLK(NULL, "armxor_ck", &armxor_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK(NULL, "armtim_ck", &armtim_ck.clk.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap_wdt", "fck", &armwdt_ck.clk.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap_wdt", "ick", &armper_ck.clk.hw, CK_16XX), + CLK("omap_wdt", "ick", &dummy_ck.hw, CK_1510 | CK_310), + CLK(NULL, "arminth_ck", &arminth_ck1510.hw, CK_1510 | CK_310), + CLK(NULL, "arminth_ck", &arminth_ck16xx.hw, CK_16XX), + /* CK_GEN2 clocks */ + CLK(NULL, "dsp_ck", &dsp_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "dspmmu_ck", &dspmmu_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "dspper_ck", &dspper_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "dspxor_ck", &dspxor_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "dsptim_ck", &dsptim_ck.hw, CK_16XX | CK_1510 | CK_310), + /* CK_GEN3 clocks */ + CLK(NULL, "tc_ck", &tc_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK(NULL, "tipb_ck", &tipb_ck.hw, CK_1510 | CK_310), + CLK(NULL, "l3_ocpi_ck", &l3_ocpi_ck.hw, CK_16XX | CK_7XX), + CLK(NULL, "tc1_ck", &tc1_ck.hw, CK_16XX), + CLK(NULL, "tc2_ck", &tc2_ck.hw, CK_16XX), + CLK(NULL, "dma_ck", &dma_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "dma_lcdfree_ck", &dma_lcdfree_ck.hw, CK_16XX), + CLK(NULL, "api_ck", &api_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK(NULL, "lb_ck", &lb_ck.clk.hw, CK_1510 | CK_310), + CLK(NULL, "rhea1_ck", &rhea1_ck.hw, CK_16XX), + CLK(NULL, "rhea2_ck", &rhea2_ck.hw, CK_16XX), + CLK(NULL, "lcd_ck", &lcd_ck_16xx.hw, CK_16XX | CK_7XX), + CLK(NULL, "lcd_ck", &lcd_ck_1510.clk.hw, CK_1510 | CK_310), + /* ULPD clocks */ + CLK(NULL, "uart1_ck", &uart1_1510.hw, CK_1510 | CK_310), + CLK(NULL, "uart1_ck", &uart1_16xx.clk.hw, CK_16XX), + CLK(NULL, "uart1_ck", &uart1_7xx.hw, CK_7XX), + CLK(NULL, "uart2_ck", &uart2_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "uart2_ck", &uart2_7xx.hw, CK_7XX), + CLK(NULL, "uart3_ck", &uart3_1510.hw, CK_1510 | CK_310), + CLK(NULL, "uart3_ck", &uart3_16xx.clk.hw, CK_16XX), + CLK(NULL, "usb_clko", &usb_clko.hw, CK_16XX | CK_1510 | CK_310), + CLK(NULL, "usb_hhc_ck", &usb_hhc_ck1510.hw, CK_1510 | CK_310), + CLK(NULL, "usb_hhc_ck", &usb_hhc_ck16xx.hw, CK_16XX), + CLK(NULL, "usb_dc_ck", &usb_dc_ck.hw, CK_16XX | CK_7XX), + CLK(NULL, "mclk", &mclk_1510.hw, CK_1510 | CK_310), + CLK(NULL, "mclk", &mclk_16xx.hw, CK_16XX), + CLK(NULL, "bclk", &bclk_1510.hw, CK_1510 | CK_310), + CLK(NULL, "bclk", &bclk_16xx.hw, CK_16XX), + CLK("mmci-omap.0", "fck", &mmc1_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK("mmci-omap.0", "fck", &mmc3_ck.hw, CK_7XX), + CLK("mmci-omap.0", "ick", &armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK("mmci-omap.1", "fck", &mmc2_ck.hw, CK_16XX), + CLK("mmci-omap.1", "ick", &armper_ck.clk.hw, CK_16XX), + /* Virtual clocks */ + CLK(NULL, "mpu", &virtual_ck_mpu.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap_i2c.1", "fck", &i2c_fck.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX), + CLK("omap_i2c.1", "ick", &i2c_ick.hw, CK_16XX), + CLK("omap_i2c.1", "ick", &dummy_ck.hw, CK_1510 | CK_310 | CK_7XX), + CLK("omap1_spi100k.1", "fck", &dummy_ck.hw, CK_7XX), + CLK("omap1_spi100k.1", "ick", &dummy_ck.hw, CK_7XX), + CLK("omap1_spi100k.2", "fck", &dummy_ck.hw, CK_7XX), + CLK("omap1_spi100k.2", "ick", &dummy_ck.hw, CK_7XX), + CLK("omap_uwire", "fck", &armxor_ck.clk.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap-mcbsp.1", "ick", &dspper_ck.hw, CK_16XX), + CLK("omap-mcbsp.1", "ick", &dummy_ck.hw, CK_1510 | CK_310), + CLK("omap-mcbsp.2", "ick", &armper_ck.clk.hw, CK_16XX), + CLK("omap-mcbsp.2", "ick", &dummy_ck.hw, CK_1510 | CK_310), + CLK("omap-mcbsp.3", "ick", &dspper_ck.hw, CK_16XX), + CLK("omap-mcbsp.3", "ick", &dummy_ck.hw, CK_1510 | CK_310), + CLK("omap-mcbsp.1", "fck", &dspxor_ck.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap-mcbsp.2", "fck", &armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310), + CLK("omap-mcbsp.3", "fck", &dspxor_ck.hw, CK_16XX | CK_1510 | CK_310), +}; + +/* + * init + */ + +static void __init omap1_show_rates(void) +{ + pr_notice("Clocking rate (xtal/DPLL1/MPU): %ld.%01ld/%ld.%01ld/%ld.%01ld MHz\n", + ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10, + ck_dpll1.rate / 1000000, (ck_dpll1.rate / 100000) % 10, + arm_ck.rate / 1000000, (arm_ck.rate / 100000) % 10); +} + +u32 cpu_mask; + +int __init omap1_clk_init(void) +{ + struct omap_clk *c; + u32 reg; + +#ifdef CONFIG_DEBUG_LL + /* Make sure UART clocks are enabled early */ + if (cpu_is_omap16xx()) + omap_writel(omap_readl(MOD_CONF_CTRL_0) | + CONF_MOD_UART1_CLK_MODE_R | + CONF_MOD_UART3_CLK_MODE_R, MOD_CONF_CTRL_0); +#endif + + /* USB_REQ_EN will be disabled later if necessary (usb_dc_ck) */ + reg = omap_readw(SOFT_REQ_REG) & (1 << 4); + omap_writew(reg, SOFT_REQ_REG); + if (!cpu_is_omap15xx()) + omap_writew(0, SOFT_REQ_REG2); + + /* By default all idlect1 clocks are allowed to idle */ + arm_idlect1_mask = ~0; + + cpu_mask = 0; + if (cpu_is_omap1710()) + cpu_mask |= CK_1710; + if (cpu_is_omap16xx()) + cpu_mask |= CK_16XX; + if (cpu_is_omap1510()) + cpu_mask |= CK_1510; + if (cpu_is_omap7xx()) + cpu_mask |= CK_7XX; + if (cpu_is_omap310()) + cpu_mask |= CK_310; + + /* Pointers to these clocks are needed by code in clock.c */ + api_ck_p = &api_ck.clk; + ck_dpll1_p = &ck_dpll1; + ck_ref_p = &ck_ref; + + if (cpu_is_omap7xx()) + ck_ref.rate = 13000000; + + pr_info("Clocks: ARM_SYSST: 0x%04x DPLL_CTL: 0x%04x ARM_CKCTL: 0x%04x\n", + omap_readw(ARM_SYSST), omap_readw(DPLL_CTL), + omap_readw(ARM_CKCTL)); + + /* We want to be in synchronous scalable mode */ + omap_writew(0x1000, ARM_SYSST); + + + /* + * Initially use the values set by bootloader. Determine PLL rate and + * recalculate dependent clocks as if kernel had changed PLL or + * divisors. See also omap1_clk_late_init() that can reprogram dpll1 + * after the SRAM is initialized. + */ + { + unsigned pll_ctl_val = omap_readw(DPLL_CTL); + + ck_dpll1.rate = ck_ref.rate; /* Base xtal rate */ + if (pll_ctl_val & 0x10) { + /* PLL enabled, apply multiplier and divisor */ + if (pll_ctl_val & 0xf80) + ck_dpll1.rate *= (pll_ctl_val & 0xf80) >> 7; + ck_dpll1.rate /= ((pll_ctl_val & 0x60) >> 5) + 1; + } else { + /* PLL disabled, apply bypass divisor */ + switch (pll_ctl_val & 0xc) { + case 0: + break; + case 0x4: + ck_dpll1.rate /= 2; + break; + default: + ck_dpll1.rate /= 4; + break; + } + } + } + + if (machine_is_omap_perseus2() || machine_is_omap_fsample()) { + /* Select slicer output as OMAP input clock */ + omap_writew(omap_readw(OMAP7XX_PCC_UPLD_CTRL) & ~0x1, + OMAP7XX_PCC_UPLD_CTRL); + } + + /* Amstrad Delta wants BCLK high when inactive */ + if (machine_is_ams_delta()) + omap_writel(omap_readl(ULPD_CLOCK_CTRL) | + (1 << SDW_MCLK_INV_BIT), + ULPD_CLOCK_CTRL); + + /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */ + /* (on 730, bit 13 must not be cleared) */ + if (cpu_is_omap7xx()) + omap_writew(omap_readw(ARM_CKCTL) & 0x2fff, ARM_CKCTL); + else + omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); + + /* Put DSP/MPUI into reset until needed */ + omap_writew(0, ARM_RSTCT1); + omap_writew(1, ARM_RSTCT2); + omap_writew(0x400, ARM_IDLECT1); + + /* + * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) + * of the ARM_IDLECT2 register must be set to zero. The power-on + * default value of this bit is one. + */ + omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ + + for (c = omap_clks; c < omap_clks + ARRAY_SIZE(omap_clks); c++) { + if (!(c->cpu & cpu_mask)) + continue; + + if (c->lk.clk_hw->init) { /* NULL if provider already registered */ + const struct clk_init_data *init = c->lk.clk_hw->init; + const char *name = c->lk.clk_hw->init->name; + int err; + + err = clk_hw_register(NULL, c->lk.clk_hw); + if (err < 0) { + pr_err("failed to register clock \"%s\"! (%d)\n", name, err); + /* may be tried again, restore init data */ + c->lk.clk_hw->init = init; + continue; + } + } + + clk_hw_register_clkdev(c->lk.clk_hw, c->lk.con_id, c->lk.dev_id); + } + + omap1_show_rates(); + + return 0; +} + +#define OMAP1_DPLL1_SANE_VALUE 60000000 + +void __init omap1_clk_late_init(void) +{ + unsigned long rate = ck_dpll1.rate; + + /* Find the highest supported frequency and enable it */ + if (omap1_select_table_rate(&virtual_ck_mpu, ~0, arm_ck.rate)) { + pr_err("System frequencies not set, using default. Check your config.\n"); + /* + * Reprogramming the DPLL is tricky, it must be done from SRAM. + */ + omap_sram_reprogram_clock(0x2290, 0x0005); + ck_dpll1.rate = OMAP1_DPLL1_SANE_VALUE; + } + propagate_rate(&ck_dpll1); + omap1_show_rates(); + loops_per_jiffy = cpufreq_scale(loops_per_jiffy, rate, ck_dpll1.rate); +} diff --git a/arch/arm/mach-omap1/common.h b/arch/arm/mach-omap1/common.h new file mode 100644 index 000000000..5ceff05e1 --- /dev/null +++ b/arch/arm/mach-omap1/common.h @@ -0,0 +1,105 @@ +/* + * + * Header for code common to all OMAP1 machines. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_COMMON_H +#define __ARCH_ARM_MACH_OMAP1_COMMON_H + +#include <linux/platform_data/i2c-omap.h> +#include <linux/reboot.h> + +#include <asm/exception.h> + +#include "irqs.h" +#include "soc.h" +#include "i2c.h" + +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) +void omap7xx_map_io(void); +#else +static inline void omap7xx_map_io(void) +{ +} +#endif + +#ifdef CONFIG_ARCH_OMAP15XX +void omap1510_fpga_init_irq(void); +void omap15xx_map_io(void); +#else +static inline void omap1510_fpga_init_irq(void) +{ +} +static inline void omap15xx_map_io(void) +{ +} +#endif + +#ifdef CONFIG_ARCH_OMAP16XX +void omap16xx_map_io(void); +#else +static inline void omap16xx_map_io(void) +{ +} +#endif + +#ifdef CONFIG_OMAP_SERIAL_WAKE +int omap_serial_wakeup_init(void); +#else +static inline int omap_serial_wakeup_init(void) +{ + return 0; +} +#endif + +void omap1_init_early(void); +void omap1_init_irq(void); +void __exception_irq_entry omap1_handle_irq(struct pt_regs *regs); +void omap1_init_late(void); +void omap1_restart(enum reboot_mode, const char *); + +extern void __init omap_check_revision(void); + +struct nand_chip; +extern void omap1_nand_cmd_ctl(struct nand_chip *this, int cmd, + unsigned int ctrl); + +extern void omap1_timer_init(void); +#ifdef CONFIG_OMAP_32K_TIMER +extern int omap_32k_timer_init(void); +#else +static inline int __init omap_32k_timer_init(void) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_ARCH_OMAP16XX +extern int ocpi_enable(void); +#else +static inline int ocpi_enable(void) { return 0; } +#endif + +extern u32 omap1_get_reset_sources(void); + +#endif /* __ARCH_ARM_MACH_OMAP1_COMMON_H */ diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c new file mode 100644 index 000000000..80e947705 --- /dev/null +++ b/arch/arm/mach-omap1/devices.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/arch/arm/mach-omap1/devices.c + * + * OMAP1 platform device setup/initialization + */ + +#include <linux/dma-mapping.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> + +#include <linux/platform_data/omap-wd-timer.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/mach/map.h> + +#include "tc.h" +#include "mux.h" + +#include "omap7xx.h" +#include "hardware.h" +#include "common.h" +#include "clock.h" +#include "mmc.h" +#include "sram.h" + +#if IS_ENABLED(CONFIG_RTC_DRV_OMAP) + +#define OMAP_RTC_BASE 0xfffb4800 + +static struct resource rtc_resources[] = { + { + .start = OMAP_RTC_BASE, + .end = OMAP_RTC_BASE + 0x5f, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_RTC_TIMER, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device omap_rtc_device = { + .name = "omap_rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static void omap_init_rtc(void) +{ + (void) platform_device_register(&omap_rtc_device); +} +#else +static inline void omap_init_rtc(void) {} +#endif + +static inline void omap_init_mbox(void) { } + +/*-------------------------------------------------------------------------*/ + +#if IS_ENABLED(CONFIG_MMC_OMAP) + +static inline void omap1_mmc_mux(struct omap_mmc_platform_data *mmc_controller, + int controller_nr) +{ + if (controller_nr == 0) { + if (cpu_is_omap7xx()) { + omap_cfg_reg(MMC_7XX_CMD); + omap_cfg_reg(MMC_7XX_CLK); + omap_cfg_reg(MMC_7XX_DAT0); + } else { + omap_cfg_reg(MMC_CMD); + omap_cfg_reg(MMC_CLK); + omap_cfg_reg(MMC_DAT0); + } + + if (cpu_is_omap1710()) { + omap_cfg_reg(M15_1710_MMC_CLKI); + omap_cfg_reg(P19_1710_MMC_CMDDIR); + omap_cfg_reg(P20_1710_MMC_DATDIR0); + } + if (mmc_controller->slots[0].wires == 4 && !cpu_is_omap7xx()) { + omap_cfg_reg(MMC_DAT1); + /* NOTE: DAT2 can be on W10 (here) or M15 */ + if (!mmc_controller->slots[0].nomux) + omap_cfg_reg(MMC_DAT2); + omap_cfg_reg(MMC_DAT3); + } + } + + /* Block 2 is on newer chips, and has many pinout options */ + if (cpu_is_omap16xx() && controller_nr == 1) { + if (!mmc_controller->slots[1].nomux) { + omap_cfg_reg(Y8_1610_MMC2_CMD); + omap_cfg_reg(Y10_1610_MMC2_CLK); + omap_cfg_reg(R18_1610_MMC2_CLKIN); + omap_cfg_reg(W8_1610_MMC2_DAT0); + if (mmc_controller->slots[1].wires == 4) { + omap_cfg_reg(V8_1610_MMC2_DAT1); + omap_cfg_reg(W15_1610_MMC2_DAT2); + omap_cfg_reg(R10_1610_MMC2_DAT3); + } + + /* These are needed for the level shifter */ + omap_cfg_reg(V9_1610_MMC2_CMDDIR); + omap_cfg_reg(V5_1610_MMC2_DATDIR0); + omap_cfg_reg(W19_1610_MMC2_DATDIR1); + } + + /* Feedback clock must be set on OMAP-1710 MMC2 */ + if (cpu_is_omap1710()) + omap_writel(omap_readl(MOD_CONF_CTRL_1) | (1 << 24), + MOD_CONF_CTRL_1); + } +} + +#define OMAP_MMC_NR_RES 4 + +/* + * Register MMC devices. + */ +static int __init omap_mmc_add(const char *name, int id, unsigned long base, + unsigned long size, unsigned int irq, + unsigned rx_req, unsigned tx_req, + struct omap_mmc_platform_data *data) +{ + struct platform_device *pdev; + struct resource res[OMAP_MMC_NR_RES]; + int ret; + + pdev = platform_device_alloc(name, id); + if (!pdev) + return -ENOMEM; + + memset(res, 0, OMAP_MMC_NR_RES * sizeof(struct resource)); + res[0].start = base; + res[0].end = base + size - 1; + res[0].flags = IORESOURCE_MEM; + res[1].start = res[1].end = irq; + res[1].flags = IORESOURCE_IRQ; + res[2].start = rx_req; + res[2].name = "rx"; + res[2].flags = IORESOURCE_DMA; + res[3].start = tx_req; + res[3].name = "tx"; + res[3].flags = IORESOURCE_DMA; + + if (cpu_is_omap7xx()) + data->slots[0].features = MMC_OMAP7XX; + if (cpu_is_omap15xx()) + data->slots[0].features = MMC_OMAP15XX; + if (cpu_is_omap16xx()) + data->slots[0].features = MMC_OMAP16XX; + + ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); + if (ret == 0) + ret = platform_device_add_data(pdev, data, sizeof(*data)); + if (ret) + goto fail; + + ret = platform_device_add(pdev); + if (ret) + goto fail; + + /* return device handle to board setup code */ + data->dev = &pdev->dev; + return 0; + +fail: + platform_device_put(pdev); + return ret; +} + +void __init omap1_init_mmc(struct omap_mmc_platform_data **mmc_data, + int nr_controllers) +{ + int i; + + for (i = 0; i < nr_controllers; i++) { + unsigned long base, size; + unsigned rx_req, tx_req; + unsigned int irq = 0; + + if (!mmc_data[i]) + continue; + + omap1_mmc_mux(mmc_data[i], i); + + switch (i) { + case 0: + base = OMAP1_MMC1_BASE; + irq = INT_MMC; + rx_req = 22; + tx_req = 21; + break; + case 1: + if (!cpu_is_omap16xx()) + return; + base = OMAP1_MMC2_BASE; + irq = INT_1610_MMC2; + rx_req = 55; + tx_req = 54; + break; + default: + continue; + } + size = OMAP1_MMC_SIZE; + + omap_mmc_add("mmci-omap", i, base, size, irq, + rx_req, tx_req, mmc_data[i]); + } +} + +#endif + +/*-------------------------------------------------------------------------*/ + +/* OMAP7xx SPI support */ +#if IS_ENABLED(CONFIG_SPI_OMAP_100K) + +struct platform_device omap_spi1 = { + .name = "omap1_spi100k", + .id = 1, +}; + +struct platform_device omap_spi2 = { + .name = "omap1_spi100k", + .id = 2, +}; + +static void omap_init_spi100k(void) +{ + if (!cpu_is_omap7xx()) + return; + + omap_spi1.dev.platform_data = ioremap(OMAP7XX_SPI1_BASE, 0x7ff); + if (omap_spi1.dev.platform_data) + platform_device_register(&omap_spi1); + + omap_spi2.dev.platform_data = ioremap(OMAP7XX_SPI2_BASE, 0x7ff); + if (omap_spi2.dev.platform_data) + platform_device_register(&omap_spi2); +} + +#else +static inline void omap_init_spi100k(void) +{ +} +#endif + +/*-------------------------------------------------------------------------*/ + +static inline void omap_init_sti(void) {} + +/* Numbering for the SPI-capable controllers when used for SPI: + * spi = 1 + * uwire = 2 + * mmc1..2 = 3..4 + * mcbsp1..3 = 5..7 + */ + +#if IS_ENABLED(CONFIG_SPI_OMAP_UWIRE) + +#define OMAP_UWIRE_BASE 0xfffb3000 + +static struct resource uwire_resources[] = { + { + .start = OMAP_UWIRE_BASE, + .end = OMAP_UWIRE_BASE + 0x20, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_uwire_device = { + .name = "omap_uwire", + .id = -1, + .num_resources = ARRAY_SIZE(uwire_resources), + .resource = uwire_resources, +}; + +static void omap_init_uwire(void) +{ + /* FIXME define and use a boot tag; not all boards will be hooking + * up devices to the microwire controller, and multi-board configs + * mean that CONFIG_SPI_OMAP_UWIRE may be configured anyway... + */ + + /* board-specific code must configure chipselects (only a few + * are normally used) and SCLK/SDI/SDO (each has two choices). + */ + (void) platform_device_register(&omap_uwire_device); +} +#else +static inline void omap_init_uwire(void) {} +#endif + + +#define OMAP1_RNG_BASE 0xfffe5000 + +static struct resource omap1_rng_resources[] = { + { + .start = OMAP1_RNG_BASE, + .end = OMAP1_RNG_BASE + 0x4f, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap1_rng_device = { + .name = "omap_rng", + .id = -1, + .num_resources = ARRAY_SIZE(omap1_rng_resources), + .resource = omap1_rng_resources, +}; + +static void omap1_init_rng(void) +{ + if (!cpu_is_omap16xx()) + return; + + (void) platform_device_register(&omap1_rng_device); +} + +/*-------------------------------------------------------------------------*/ + +/* + * This gets called after board-specific INIT_MACHINE, and initializes most + * on-chip peripherals accessible on this board (except for few like USB): + * + * (a) Does any "standard config" pin muxing needed. Board-specific + * code will have muxed GPIO pins and done "nonstandard" setup; + * that code could live in the boot loader. + * (b) Populating board-specific platform_data with the data drivers + * rely on to handle wiring variations. + * (c) Creating platform devices as meaningful on this board and + * with this kernel configuration. + * + * Claiming GPIOs, and setting their direction and initial values, is the + * responsibility of the device drivers. So is responding to probe(). + * + * Board-specific knowledge like creating devices or pin setup is to be + * kept out of drivers as much as possible. In particular, pin setup + * may be handled by the boot loader, and drivers should expect it will + * normally have been done by the time they're probed. + */ +static int __init omap1_init_devices(void) +{ + if (!cpu_class_is_omap1()) + return -ENODEV; + + omap1_sram_init(); + omap1_clk_late_init(); + + /* please keep these calls, and their implementations above, + * in alphabetical order so they're easier to sort through. + */ + + omap_init_mbox(); + omap_init_rtc(); + omap_init_spi100k(); + omap_init_sti(); + omap_init_uwire(); + omap1_init_rng(); + + return 0; +} +arch_initcall(omap1_init_devices); + +#if IS_ENABLED(CONFIG_OMAP_WATCHDOG) + +static struct resource wdt_resources[] = { + { + .start = 0xfffeb000, + .end = 0xfffeb07F, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_wdt_device = { + .name = "omap_wdt", + .id = -1, + .num_resources = ARRAY_SIZE(wdt_resources), + .resource = wdt_resources, +}; + +static int __init omap_init_wdt(void) +{ + struct omap_wd_timer_platform_data pdata; + int ret; + + if (!cpu_is_omap16xx()) + return -ENODEV; + + pdata.read_reset_sources = omap1_get_reset_sources; + + ret = platform_device_register(&omap_wdt_device); + if (!ret) { + ret = platform_device_add_data(&omap_wdt_device, &pdata, + sizeof(pdata)); + if (ret) + platform_device_del(&omap_wdt_device); + } + + return ret; +} +subsys_initcall(omap_init_wdt); +#endif diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c new file mode 100644 index 000000000..c3f280c3c --- /dev/null +++ b/arch/arm/mach-omap1/dma.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP1/OMAP7xx - specific DMA driver + * + * Copyright (C) 2003 - 2008 Nokia Corporation + * Author: Juha Yrjölä <juha.yrjola@nokia.com> + * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> + * Graphics DMA and LCD DMA graphics tranformations + * by Imre Deak <imre.deak@nokia.com> + * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. + * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Converted DMA library into platform driver + * - G, Manjunath Kondaiah <manjugk@ti.com> + */ + +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/omap-dma.h> +#include "tc.h" + +#include "soc.h" + +#define OMAP1_DMA_BASE (0xfffed800) + +static u32 enable_1510_mode; + +static const struct omap_dma_reg reg_map[] = { + [GCR] = { 0x0400, 0x00, OMAP_DMA_REG_16BIT }, + [GSCR] = { 0x0404, 0x00, OMAP_DMA_REG_16BIT }, + [GRST1] = { 0x0408, 0x00, OMAP_DMA_REG_16BIT }, + [HW_ID] = { 0x0442, 0x00, OMAP_DMA_REG_16BIT }, + [PCH2_ID] = { 0x0444, 0x00, OMAP_DMA_REG_16BIT }, + [PCH0_ID] = { 0x0446, 0x00, OMAP_DMA_REG_16BIT }, + [PCH1_ID] = { 0x0448, 0x00, OMAP_DMA_REG_16BIT }, + [PCHG_ID] = { 0x044a, 0x00, OMAP_DMA_REG_16BIT }, + [PCHD_ID] = { 0x044c, 0x00, OMAP_DMA_REG_16BIT }, + [CAPS_0] = { 0x044e, 0x00, OMAP_DMA_REG_2X16BIT }, + [CAPS_1] = { 0x0452, 0x00, OMAP_DMA_REG_2X16BIT }, + [CAPS_2] = { 0x0456, 0x00, OMAP_DMA_REG_16BIT }, + [CAPS_3] = { 0x0458, 0x00, OMAP_DMA_REG_16BIT }, + [CAPS_4] = { 0x045a, 0x00, OMAP_DMA_REG_16BIT }, + [PCH2_SR] = { 0x0460, 0x00, OMAP_DMA_REG_16BIT }, + [PCH0_SR] = { 0x0480, 0x00, OMAP_DMA_REG_16BIT }, + [PCH1_SR] = { 0x0482, 0x00, OMAP_DMA_REG_16BIT }, + [PCHD_SR] = { 0x04c0, 0x00, OMAP_DMA_REG_16BIT }, + + /* Common Registers */ + [CSDP] = { 0x0000, 0x40, OMAP_DMA_REG_16BIT }, + [CCR] = { 0x0002, 0x40, OMAP_DMA_REG_16BIT }, + [CICR] = { 0x0004, 0x40, OMAP_DMA_REG_16BIT }, + [CSR] = { 0x0006, 0x40, OMAP_DMA_REG_16BIT }, + [CEN] = { 0x0010, 0x40, OMAP_DMA_REG_16BIT }, + [CFN] = { 0x0012, 0x40, OMAP_DMA_REG_16BIT }, + [CSFI] = { 0x0014, 0x40, OMAP_DMA_REG_16BIT }, + [CSEI] = { 0x0016, 0x40, OMAP_DMA_REG_16BIT }, + [CPC] = { 0x0018, 0x40, OMAP_DMA_REG_16BIT }, /* 15xx only */ + [CSAC] = { 0x0018, 0x40, OMAP_DMA_REG_16BIT }, + [CDAC] = { 0x001a, 0x40, OMAP_DMA_REG_16BIT }, + [CDEI] = { 0x001c, 0x40, OMAP_DMA_REG_16BIT }, + [CDFI] = { 0x001e, 0x40, OMAP_DMA_REG_16BIT }, + [CLNK_CTRL] = { 0x0028, 0x40, OMAP_DMA_REG_16BIT }, + + /* Channel specific register offsets */ + [CSSA] = { 0x0008, 0x40, OMAP_DMA_REG_2X16BIT }, + [CDSA] = { 0x000c, 0x40, OMAP_DMA_REG_2X16BIT }, + [COLOR] = { 0x0020, 0x40, OMAP_DMA_REG_2X16BIT }, + [CCR2] = { 0x0024, 0x40, OMAP_DMA_REG_16BIT }, + [LCH_CTRL] = { 0x002a, 0x40, OMAP_DMA_REG_16BIT }, +}; + +static struct resource res[] __initdata = { + [0] = { + .start = OMAP1_DMA_BASE, + .end = OMAP1_DMA_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "0", + .start = INT_DMA_CH0_6, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "1", + .start = INT_DMA_CH1_7, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .name = "2", + .start = INT_DMA_CH2_8, + .flags = IORESOURCE_IRQ, + }, + [4] = { + .name = "3", + .start = INT_DMA_CH3, + .flags = IORESOURCE_IRQ, + }, + [5] = { + .name = "4", + .start = INT_DMA_CH4, + .flags = IORESOURCE_IRQ, + }, + [6] = { + .name = "5", + .start = INT_DMA_CH5, + .flags = IORESOURCE_IRQ, + }, + /* Handled in lcd_dma.c */ + [7] = { + .name = "6", + .start = INT_1610_DMA_CH6, + .flags = IORESOURCE_IRQ, + }, + /* irq's for omap16xx and omap7xx */ + [8] = { + .name = "7", + .start = INT_1610_DMA_CH7, + .flags = IORESOURCE_IRQ, + }, + [9] = { + .name = "8", + .start = INT_1610_DMA_CH8, + .flags = IORESOURCE_IRQ, + }, + [10] = { + .name = "9", + .start = INT_1610_DMA_CH9, + .flags = IORESOURCE_IRQ, + }, + [11] = { + .name = "10", + .start = INT_1610_DMA_CH10, + .flags = IORESOURCE_IRQ, + }, + [12] = { + .name = "11", + .start = INT_1610_DMA_CH11, + .flags = IORESOURCE_IRQ, + }, + [13] = { + .name = "12", + .start = INT_1610_DMA_CH12, + .flags = IORESOURCE_IRQ, + }, + [14] = { + .name = "13", + .start = INT_1610_DMA_CH13, + .flags = IORESOURCE_IRQ, + }, + [15] = { + .name = "14", + .start = INT_1610_DMA_CH14, + .flags = IORESOURCE_IRQ, + }, + [16] = { + .name = "15", + .start = INT_1610_DMA_CH15, + .flags = IORESOURCE_IRQ, + }, + [17] = { + .name = "16", + .start = INT_DMA_LCD, + .flags = IORESOURCE_IRQ, + }, +}; + +static void __iomem *dma_base; +static inline void dma_write(u32 val, int reg, int lch) +{ + void __iomem *addr = dma_base; + + addr += reg_map[reg].offset; + addr += reg_map[reg].stride * lch; + + __raw_writew(val, addr); + if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT) + __raw_writew(val >> 16, addr + 2); +} + +static inline u32 dma_read(int reg, int lch) +{ + void __iomem *addr = dma_base; + uint32_t val; + + addr += reg_map[reg].offset; + addr += reg_map[reg].stride * lch; + + val = __raw_readw(addr); + if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT) + val |= __raw_readw(addr + 2) << 16; + + return val; +} + +static void omap1_clear_lch_regs(int lch) +{ + int i; + + for (i = CPC; i <= COLOR; i += 1) + dma_write(0, i, lch); +} + +static void omap1_clear_dma(int lch) +{ + u32 l; + + l = dma_read(CCR, lch); + l &= ~OMAP_DMA_CCR_EN; + dma_write(l, CCR, lch); + + /* Clear pending interrupts */ + l = dma_read(CSR, lch); +} + +static void omap1_show_dma_caps(void) +{ + if (enable_1510_mode) { + printk(KERN_INFO "DMA support for OMAP15xx initialized\n"); + } else { + u16 w; + printk(KERN_INFO "OMAP DMA hardware version %d\n", + dma_read(HW_ID, 0)); + printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", + dma_read(CAPS_0, 0), dma_read(CAPS_1, 0), + dma_read(CAPS_2, 0), dma_read(CAPS_3, 0), + dma_read(CAPS_4, 0)); + + /* Disable OMAP 3.0/3.1 compatibility mode. */ + w = dma_read(GSCR, 0); + w |= 1 << 3; + dma_write(w, GSCR, 0); + } +} + +static unsigned configure_dma_errata(void) +{ + unsigned errata = 0; + + /* + * Erratum 3.2/3.3: sometimes 0 is returned if CSAC/CDAC is + * read before the DMA controller finished disabling the channel. + */ + if (!cpu_is_omap15xx()) + SET_DMA_ERRATA(DMA_ERRATA_3_3); + + return errata; +} + +static const struct platform_device_info omap_dma_dev_info = { + .name = "omap-dma-engine", + .id = -1, + .dma_mask = DMA_BIT_MASK(32), + .res = res, + .num_res = 1, +}; + +/* OMAP730, OMAP850 */ +static const struct dma_slave_map omap7xx_sdma_map[] = { + { "omap-mcbsp.1", "tx", SDMA_FILTER_PARAM(8) }, + { "omap-mcbsp.1", "rx", SDMA_FILTER_PARAM(9) }, + { "omap-mcbsp.2", "tx", SDMA_FILTER_PARAM(10) }, + { "omap-mcbsp.2", "rx", SDMA_FILTER_PARAM(11) }, + { "mmci-omap.0", "tx", SDMA_FILTER_PARAM(21) }, + { "mmci-omap.0", "rx", SDMA_FILTER_PARAM(22) }, + { "omap_udc", "rx0", SDMA_FILTER_PARAM(26) }, + { "omap_udc", "rx1", SDMA_FILTER_PARAM(27) }, + { "omap_udc", "rx2", SDMA_FILTER_PARAM(28) }, + { "omap_udc", "tx0", SDMA_FILTER_PARAM(29) }, + { "omap_udc", "tx1", SDMA_FILTER_PARAM(30) }, + { "omap_udc", "tx2", SDMA_FILTER_PARAM(31) }, +}; + +/* OMAP1510, OMAP1610*/ +static const struct dma_slave_map omap1xxx_sdma_map[] = { + { "omap-mcbsp.1", "tx", SDMA_FILTER_PARAM(8) }, + { "omap-mcbsp.1", "rx", SDMA_FILTER_PARAM(9) }, + { "omap-mcbsp.3", "tx", SDMA_FILTER_PARAM(10) }, + { "omap-mcbsp.3", "rx", SDMA_FILTER_PARAM(11) }, + { "omap-mcbsp.2", "tx", SDMA_FILTER_PARAM(16) }, + { "omap-mcbsp.2", "rx", SDMA_FILTER_PARAM(17) }, + { "mmci-omap.0", "tx", SDMA_FILTER_PARAM(21) }, + { "mmci-omap.0", "rx", SDMA_FILTER_PARAM(22) }, + { "omap_udc", "rx0", SDMA_FILTER_PARAM(26) }, + { "omap_udc", "rx1", SDMA_FILTER_PARAM(27) }, + { "omap_udc", "rx2", SDMA_FILTER_PARAM(28) }, + { "omap_udc", "tx0", SDMA_FILTER_PARAM(29) }, + { "omap_udc", "tx1", SDMA_FILTER_PARAM(30) }, + { "omap_udc", "tx2", SDMA_FILTER_PARAM(31) }, + { "mmci-omap.1", "tx", SDMA_FILTER_PARAM(54) }, + { "mmci-omap.1", "rx", SDMA_FILTER_PARAM(55) }, +}; + +static struct omap_system_dma_plat_info dma_plat_info __initdata = { + .reg_map = reg_map, + .channel_stride = 0x40, + .show_dma_caps = omap1_show_dma_caps, + .clear_lch_regs = omap1_clear_lch_regs, + .clear_dma = omap1_clear_dma, + .dma_write = dma_write, + .dma_read = dma_read, +}; + +static int __init omap1_system_dma_init(void) +{ + struct omap_system_dma_plat_info p; + struct omap_dma_dev_attr *d; + struct platform_device *pdev, *dma_pdev; + int ret; + + pdev = platform_device_alloc("omap_dma_system", 0); + if (!pdev) { + pr_err("%s: Unable to device alloc for dma\n", + __func__); + return -ENOMEM; + } + + dma_base = ioremap(res[0].start, resource_size(&res[0])); + if (!dma_base) { + pr_err("%s: Unable to ioremap\n", __func__); + ret = -ENODEV; + goto exit_device_put; + } + + ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", + __func__, pdev->name, pdev->id); + goto exit_iounmap; + } + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + ret = -ENOMEM; + goto exit_iounmap; + } + + /* Valid attributes for omap1 plus processors */ + if (cpu_is_omap15xx()) + d->dev_caps = ENABLE_1510_MODE; + enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; + + if (cpu_is_omap16xx()) + d->dev_caps = ENABLE_16XX_MODE; + + d->dev_caps |= SRC_PORT; + d->dev_caps |= DST_PORT; + d->dev_caps |= SRC_INDEX; + d->dev_caps |= DST_INDEX; + d->dev_caps |= IS_BURST_ONLY4; + d->dev_caps |= CLEAR_CSR_ON_READ; + d->dev_caps |= IS_WORD_16; + + /* available logical channels */ + if (cpu_is_omap15xx()) { + d->lch_count = 9; + } else { + if (d->dev_caps & ENABLE_1510_MODE) + d->lch_count = 9; + else + d->lch_count = 16; + } + + p = dma_plat_info; + p.dma_attr = d; + p.errata = configure_dma_errata(); + + if (cpu_is_omap7xx()) { + p.slave_map = omap7xx_sdma_map; + p.slavecnt = ARRAY_SIZE(omap7xx_sdma_map); + } else { + p.slave_map = omap1xxx_sdma_map; + p.slavecnt = ARRAY_SIZE(omap1xxx_sdma_map); + } + + ret = platform_device_add_data(pdev, &p, sizeof(p)); + if (ret) { + dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", + __func__, pdev->name, pdev->id); + goto exit_release_d; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n", + __func__, pdev->name, pdev->id); + goto exit_release_d; + } + + dma_pdev = platform_device_register_full(&omap_dma_dev_info); + if (IS_ERR(dma_pdev)) { + ret = PTR_ERR(dma_pdev); + goto exit_release_pdev; + } + + return ret; + +exit_release_pdev: + platform_device_del(pdev); +exit_release_d: + kfree(d); +exit_iounmap: + iounmap(dma_base); +exit_device_put: + platform_device_put(pdev); + + return ret; +} +arch_initcall(omap1_system_dma_init); diff --git a/arch/arm/mach-omap1/fb.c b/arch/arm/mach-omap1/fb.c new file mode 100644 index 000000000..b6e952b03 --- /dev/null +++ b/arch/arm/mach-omap1/fb.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * File: arch/arm/plat-omap/fb.c + * + * Framebuffer device registration for TI OMAP platforms + * + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/memblock.h> +#include <linux/io.h> +#include <linux/omapfb.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> + +#include <asm/mach/map.h> + +#include "irqs.h" + +#if IS_ENABLED(CONFIG_FB_OMAP) + +static bool omapfb_lcd_configured; +static struct omapfb_platform_data omapfb_config; + +static u64 omap_fb_dma_mask = ~(u32)0; + +static struct resource omap_fb_resources[] = { + { + .name = "irq", + .start = INT_LCD_CTRL, + .flags = IORESOURCE_IRQ, + }, + { + .name = "irq", + .start = INT_SOSSI_MATCH, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device omap_fb_device = { + .name = "omapfb", + .id = -1, + .dev = { + .dma_mask = &omap_fb_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &omapfb_config, + }, + .num_resources = ARRAY_SIZE(omap_fb_resources), + .resource = omap_fb_resources, +}; + +void __init omapfb_set_lcd_config(const struct omap_lcd_config *config) +{ + omapfb_config.lcd = *config; + omapfb_lcd_configured = true; +} + +static int __init omap_init_fb(void) +{ + /* + * If the board file has not set the lcd config with + * omapfb_set_lcd_config(), don't bother registering the omapfb device + */ + if (!omapfb_lcd_configured) + return 0; + + return platform_device_register(&omap_fb_device); +} + +arch_initcall(omap_init_fb); + +#else + +void __init omapfb_set_lcd_config(const struct omap_lcd_config *config) +{ +} + +#endif diff --git a/arch/arm/mach-omap1/flash.c b/arch/arm/mach-omap1/flash.c new file mode 100644 index 000000000..0a3ddb3b6 --- /dev/null +++ b/arch/arm/mach-omap1/flash.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Flash support for OMAP1 + */ + +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/soc/ti/omap1-io.h> + +#include "tc.h" + +#include "flash.h" + + +void omap1_set_vpp(struct platform_device *pdev, int enable) +{ + u32 l; + + l = omap_readl(EMIFS_CONFIG); + if (enable) + l |= OMAP_EMIFS_CONFIG_WP; + else + l &= ~OMAP_EMIFS_CONFIG_WP; + omap_writel(l, EMIFS_CONFIG); +} diff --git a/arch/arm/mach-omap1/flash.h b/arch/arm/mach-omap1/flash.h new file mode 100644 index 000000000..7077224f6 --- /dev/null +++ b/arch/arm/mach-omap1/flash.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Flash support for OMAP1 + */ + +#ifndef __OMAP_FLASH_H +#define __OMAP_FLASH_H + +#include <linux/mtd/map.h> + +struct platform_device; +extern void omap1_set_vpp(struct platform_device *pdev, int enable); + +#endif diff --git a/arch/arm/mach-omap1/fpga.c b/arch/arm/mach-omap1/fpga.c new file mode 100644 index 000000000..4c71a1959 --- /dev/null +++ b/arch/arm/mach-omap1/fpga.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/fpga.c + * + * Interrupt handler for OMAP-1510 Innovator FPGA + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> + */ + +#include <linux/types.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <asm/mach/irq.h> + +#include "hardware.h" +#include "iomap.h" +#include "common.h" +#include "fpga.h" + +static void fpga_mask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE; + + if (irq < 8) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) + & ~(1 << irq)), OMAP1510_FPGA_IMR_LO); + else if (irq < 16) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) + & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); + else + __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) + & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2); +} + + +static inline u32 get_fpga_unmasked_irqs(void) +{ + return + ((__raw_readb(OMAP1510_FPGA_ISR_LO) & + __raw_readb(OMAP1510_FPGA_IMR_LO))) | + ((__raw_readb(OMAP1510_FPGA_ISR_HI) & + __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) | + ((__raw_readb(INNOVATOR_FPGA_ISR2) & + __raw_readb(INNOVATOR_FPGA_IMR2)) << 16); +} + + +static void fpga_ack_irq(struct irq_data *d) +{ + /* Don't need to explicitly ACK FPGA interrupts */ +} + +static void fpga_unmask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE; + + if (irq < 8) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), + OMAP1510_FPGA_IMR_LO); + else if (irq < 16) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) + | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); + else + __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) + | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2); +} + +static void fpga_mask_ack_irq(struct irq_data *d) +{ + fpga_mask_irq(d); + fpga_ack_irq(d); +} + +static void innovator_fpga_IRQ_demux(struct irq_desc *desc) +{ + u32 stat; + int fpga_irq; + + stat = get_fpga_unmasked_irqs(); + + if (!stat) + return; + + for (fpga_irq = OMAP_FPGA_IRQ_BASE; + (fpga_irq < OMAP_FPGA_IRQ_END) && stat; + fpga_irq++, stat >>= 1) { + if (stat & 1) { + generic_handle_irq(fpga_irq); + } + } +} + +static struct irq_chip omap_fpga_irq_ack = { + .name = "FPGA-ack", + .irq_ack = fpga_mask_ack_irq, + .irq_mask = fpga_mask_irq, + .irq_unmask = fpga_unmask_irq, +}; + + +static struct irq_chip omap_fpga_irq = { + .name = "FPGA", + .irq_ack = fpga_ack_irq, + .irq_mask = fpga_mask_irq, + .irq_unmask = fpga_unmask_irq, +}; + +/* + * All of the FPGA interrupt request inputs except for the touchscreen are + * edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive + * interrupts are acknowledged as a side-effect of reading the interrupt + * status register from the FPGA. The edge-sensitive interrupt inputs + * cause a problem with level interrupt requests, such as Ethernet. The + * problem occurs when a level interrupt request is asserted while its + * interrupt input is masked in the FPGA, which results in a missed + * interrupt. + * + * In an attempt to workaround the problem with missed interrupts, the + * mask_ack routine for all of the FPGA interrupts has been changed from + * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt + * being serviced is left unmasked. We can do this because the FPGA cascade + * interrupt is run with all interrupts masked. + * + * Limited testing indicates that this workaround appears to be effective + * for the smc9194 Ethernet driver used on the Innovator. It should work + * on other FPGA interrupts as well, but any drivers that explicitly mask + * interrupts at the interrupt controller via disable_irq/enable_irq + * could pose a problem. + */ +void omap1510_fpga_init_irq(void) +{ + int i, res; + + __raw_writeb(0, OMAP1510_FPGA_IMR_LO); + __raw_writeb(0, OMAP1510_FPGA_IMR_HI); + __raw_writeb(0, INNOVATOR_FPGA_IMR2); + + for (i = OMAP_FPGA_IRQ_BASE; i < OMAP_FPGA_IRQ_END; i++) { + + if (i == OMAP1510_INT_FPGA_TS) { + /* + * The touchscreen interrupt is level-sensitive, so + * we'll use the regular mask_ack routine for it. + */ + irq_set_chip(i, &omap_fpga_irq_ack); + } + else { + /* + * All FPGA interrupts except the touchscreen are + * edge-sensitive, so we won't mask them. + */ + irq_set_chip(i, &omap_fpga_irq); + } + + irq_set_handler(i, handle_edge_irq); + irq_clear_status_flags(i, IRQ_NOREQUEST); + } + + /* + * The FPGA interrupt line is connected to GPIO13. Claim this pin for + * the ARM. + * + * NOTE: For general GPIO/MPUIO access and interrupts, please see + * gpio.[ch] + */ + res = gpio_request(13, "FPGA irq"); + if (res) { + pr_err("%s failed to get gpio\n", __func__); + return; + } + gpio_direction_input(13); + irq_set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING); + irq_set_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); +} diff --git a/arch/arm/mach-omap1/fpga.h b/arch/arm/mach-omap1/fpga.h new file mode 100644 index 000000000..7e7450eda --- /dev/null +++ b/arch/arm/mach-omap1/fpga.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interrupt handler for OMAP-1510 FPGA + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com> + */ + +#ifndef __ASM_ARCH_OMAP_FPGA_H +#define __ASM_ARCH_OMAP_FPGA_H + +/* + * --------------------------------------------------------------------------- + * H2/P2 Debug board FPGA + * --------------------------------------------------------------------------- + */ +/* maps in the FPGA registers and the ETHR registers */ +#define H2P2_DBG_FPGA_BASE 0xE8000000 /* VA */ +#define H2P2_DBG_FPGA_SIZE SZ_4K /* SIZE */ +#define H2P2_DBG_FPGA_START 0x04000000 /* PA */ + +#define H2P2_DBG_FPGA_ETHR_START (H2P2_DBG_FPGA_START + 0x300) +#define H2P2_DBG_FPGA_FPGA_REV IOMEM(H2P2_DBG_FPGA_BASE + 0x10) /* FPGA Revision */ +#define H2P2_DBG_FPGA_BOARD_REV IOMEM(H2P2_DBG_FPGA_BASE + 0x12) /* Board Revision */ +#define H2P2_DBG_FPGA_GPIO IOMEM(H2P2_DBG_FPGA_BASE + 0x14) /* GPIO outputs */ +#define H2P2_DBG_FPGA_LEDS IOMEM(H2P2_DBG_FPGA_BASE + 0x16) /* LEDs outputs */ +#define H2P2_DBG_FPGA_MISC_INPUTS IOMEM(H2P2_DBG_FPGA_BASE + 0x18) /* Misc inputs */ +#define H2P2_DBG_FPGA_LAN_STATUS IOMEM(H2P2_DBG_FPGA_BASE + 0x1A) /* LAN Status line */ +#define H2P2_DBG_FPGA_LAN_RESET IOMEM(H2P2_DBG_FPGA_BASE + 0x1C) /* LAN Reset line */ + +/* LEDs definition on debug board (16 LEDs, all physically green) */ +#define H2P2_DBG_FPGA_LED_GREEN (1 << 15) +#define H2P2_DBG_FPGA_LED_AMBER (1 << 14) +#define H2P2_DBG_FPGA_LED_RED (1 << 13) +#define H2P2_DBG_FPGA_LED_BLUE (1 << 12) +/* cpu0 load-meter LEDs */ +#define H2P2_DBG_FPGA_LOAD_METER (1 << 0) // A bit of fun on our board ... +#define H2P2_DBG_FPGA_LOAD_METER_SIZE 11 +#define H2P2_DBG_FPGA_LOAD_METER_MASK ((1 << H2P2_DBG_FPGA_LOAD_METER_SIZE) - 1) + +#define H2P2_DBG_FPGA_P2_LED_TIMER (1 << 0) +#define H2P2_DBG_FPGA_P2_LED_IDLE (1 << 1) + +#endif diff --git a/arch/arm/mach-omap1/gpio15xx.c b/arch/arm/mach-omap1/gpio15xx.c new file mode 100644 index 000000000..61fa26efd --- /dev/null +++ b/arch/arm/mach-omap1/gpio15xx.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP15xx specific gpio init + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: + * Charulatha V <charu@ti.com> + */ + +#include <linux/gpio.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/soc/ti/omap1-soc.h> +#include <asm/irq.h> + +#include "irqs.h" + +#define OMAP1_MPUIO_VBASE OMAP1_MPUIO_BASE +#define OMAP1510_GPIO_BASE 0xFFFCE000 + +/* gpio1 */ +static struct resource omap15xx_mpu_gpio_resources[] = { + { + .start = OMAP1_MPUIO_VBASE, + .end = OMAP1_MPUIO_VBASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MPUIO, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap15xx_mpuio_regs = { + .revision = USHRT_MAX, + .direction = OMAP_MPUIO_IO_CNTL, + .datain = OMAP_MPUIO_INPUT_LATCH, + .dataout = OMAP_MPUIO_OUTPUT, + .irqstatus = OMAP_MPUIO_GPIO_INT, + .irqenable = OMAP_MPUIO_GPIO_MASKIT, + .irqenable_inv = true, + .irqctrl = OMAP_MPUIO_GPIO_INT_EDGE, +}; + +static struct omap_gpio_platform_data omap15xx_mpu_gpio_config = { + .is_mpuio = true, + .bank_width = 16, + .bank_stride = 1, + .regs = &omap15xx_mpuio_regs, +}; + +static struct platform_device omap15xx_mpu_gpio = { + .name = "omap_gpio", + .id = 0, + .dev = { + .platform_data = &omap15xx_mpu_gpio_config, + }, + .num_resources = ARRAY_SIZE(omap15xx_mpu_gpio_resources), + .resource = omap15xx_mpu_gpio_resources, +}; + +/* gpio2 */ +static struct resource omap15xx_gpio_resources[] = { + { + .start = OMAP1510_GPIO_BASE, + .end = OMAP1510_GPIO_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GPIO_BANK1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap15xx_gpio_regs = { + .revision = USHRT_MAX, + .direction = OMAP1510_GPIO_DIR_CONTROL, + .datain = OMAP1510_GPIO_DATA_INPUT, + .dataout = OMAP1510_GPIO_DATA_OUTPUT, + .irqstatus = OMAP1510_GPIO_INT_STATUS, + .irqenable = OMAP1510_GPIO_INT_MASK, + .irqenable_inv = true, + .irqctrl = OMAP1510_GPIO_INT_CONTROL, + .pinctrl = OMAP1510_GPIO_PIN_CONTROL, +}; + +static struct omap_gpio_platform_data omap15xx_gpio_config = { + .bank_width = 16, + .regs = &omap15xx_gpio_regs, +}; + +static struct platform_device omap15xx_gpio = { + .name = "omap_gpio", + .id = 1, + .dev = { + .platform_data = &omap15xx_gpio_config, + }, + .num_resources = ARRAY_SIZE(omap15xx_gpio_resources), + .resource = omap15xx_gpio_resources, +}; + +/* + * omap15xx_gpio_init needs to be done before + * machine_init functions access gpio APIs. + * Hence omap15xx_gpio_init is a postcore_initcall. + */ +static int __init omap15xx_gpio_init(void) +{ + if (!cpu_is_omap15xx()) + return -EINVAL; + + platform_device_register(&omap15xx_mpu_gpio); + platform_device_register(&omap15xx_gpio); + + return 0; +} +postcore_initcall(omap15xx_gpio_init); diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c new file mode 100644 index 000000000..cf052714b --- /dev/null +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP16xx specific gpio init + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: + * Charulatha V <charu@ti.com> + */ + +#include <linux/gpio.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/soc/ti/omap1-io.h> + +#include "hardware.h" +#include "irqs.h" +#include "soc.h" + +#define OMAP1610_GPIO1_BASE 0xfffbe400 +#define OMAP1610_GPIO2_BASE 0xfffbec00 +#define OMAP1610_GPIO3_BASE 0xfffbb400 +#define OMAP1610_GPIO4_BASE 0xfffbbc00 +#define OMAP1_MPUIO_VBASE OMAP1_MPUIO_BASE + +/* smart idle, enable wakeup */ +#define SYSCONFIG_WORD 0x14 + +/* mpu gpio */ +static struct resource omap16xx_mpu_gpio_resources[] = { + { + .start = OMAP1_MPUIO_VBASE, + .end = OMAP1_MPUIO_VBASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MPUIO, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap16xx_mpuio_regs = { + .revision = USHRT_MAX, + .direction = OMAP_MPUIO_IO_CNTL, + .datain = OMAP_MPUIO_INPUT_LATCH, + .dataout = OMAP_MPUIO_OUTPUT, + .irqstatus = OMAP_MPUIO_GPIO_INT, + .irqenable = OMAP_MPUIO_GPIO_MASKIT, + .irqenable_inv = true, + .irqctrl = OMAP_MPUIO_GPIO_INT_EDGE, +}; + +static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = { + .is_mpuio = true, + .bank_width = 16, + .bank_stride = 1, + .regs = &omap16xx_mpuio_regs, +}; + +static struct platform_device omap16xx_mpu_gpio = { + .name = "omap_gpio", + .id = 0, + .dev = { + .platform_data = &omap16xx_mpu_gpio_config, + }, + .num_resources = ARRAY_SIZE(omap16xx_mpu_gpio_resources), + .resource = omap16xx_mpu_gpio_resources, +}; + +/* gpio1 */ +static struct resource omap16xx_gpio1_resources[] = { + { + .start = OMAP1610_GPIO1_BASE, + .end = OMAP1610_GPIO1_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GPIO_BANK1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap16xx_gpio_regs = { + .revision = OMAP1610_GPIO_REVISION, + .direction = OMAP1610_GPIO_DIRECTION, + .set_dataout = OMAP1610_GPIO_SET_DATAOUT, + .clr_dataout = OMAP1610_GPIO_CLEAR_DATAOUT, + .datain = OMAP1610_GPIO_DATAIN, + .dataout = OMAP1610_GPIO_DATAOUT, + .irqstatus = OMAP1610_GPIO_IRQSTATUS1, + .irqenable = OMAP1610_GPIO_IRQENABLE1, + .set_irqenable = OMAP1610_GPIO_SET_IRQENABLE1, + .clr_irqenable = OMAP1610_GPIO_CLEAR_IRQENABLE1, + .wkup_en = OMAP1610_GPIO_WAKEUPENABLE, + .edgectrl1 = OMAP1610_GPIO_EDGE_CTRL1, + .edgectrl2 = OMAP1610_GPIO_EDGE_CTRL2, +}; + +static struct omap_gpio_platform_data omap16xx_gpio1_config = { + .bank_width = 16, + .regs = &omap16xx_gpio_regs, +}; + +static struct platform_device omap16xx_gpio1 = { + .name = "omap_gpio", + .id = 1, + .dev = { + .platform_data = &omap16xx_gpio1_config, + }, + .num_resources = ARRAY_SIZE(omap16xx_gpio1_resources), + .resource = omap16xx_gpio1_resources, +}; + +/* gpio2 */ +static struct resource omap16xx_gpio2_resources[] = { + { + .start = OMAP1610_GPIO2_BASE, + .end = OMAP1610_GPIO2_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_1610_GPIO_BANK2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap16xx_gpio2_config = { + .bank_width = 16, + .regs = &omap16xx_gpio_regs, +}; + +static struct platform_device omap16xx_gpio2 = { + .name = "omap_gpio", + .id = 2, + .dev = { + .platform_data = &omap16xx_gpio2_config, + }, + .num_resources = ARRAY_SIZE(omap16xx_gpio2_resources), + .resource = omap16xx_gpio2_resources, +}; + +/* gpio3 */ +static struct resource omap16xx_gpio3_resources[] = { + { + .start = OMAP1610_GPIO3_BASE, + .end = OMAP1610_GPIO3_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_1610_GPIO_BANK3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap16xx_gpio3_config = { + .bank_width = 16, + .regs = &omap16xx_gpio_regs, +}; + +static struct platform_device omap16xx_gpio3 = { + .name = "omap_gpio", + .id = 3, + .dev = { + .platform_data = &omap16xx_gpio3_config, + }, + .num_resources = ARRAY_SIZE(omap16xx_gpio3_resources), + .resource = omap16xx_gpio3_resources, +}; + +/* gpio4 */ +static struct resource omap16xx_gpio4_resources[] = { + { + .start = OMAP1610_GPIO4_BASE, + .end = OMAP1610_GPIO4_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_1610_GPIO_BANK4, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap16xx_gpio4_config = { + .bank_width = 16, + .regs = &omap16xx_gpio_regs, +}; + +static struct platform_device omap16xx_gpio4 = { + .name = "omap_gpio", + .id = 4, + .dev = { + .platform_data = &omap16xx_gpio4_config, + }, + .num_resources = ARRAY_SIZE(omap16xx_gpio4_resources), + .resource = omap16xx_gpio4_resources, +}; + +static struct platform_device *omap16xx_gpio_dev[] __initdata = { + &omap16xx_mpu_gpio, + &omap16xx_gpio1, + &omap16xx_gpio2, + &omap16xx_gpio3, + &omap16xx_gpio4, +}; + +/* + * omap16xx_gpio_init needs to be done before + * machine_init functions access gpio APIs. + * Hence omap16xx_gpio_init is a postcore_initcall. + */ +static int __init omap16xx_gpio_init(void) +{ + int i; + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + struct omap_gpio_platform_data *pdata; + + if (!cpu_is_omap16xx()) + return -EINVAL; + + /* + * Enable system clock for GPIO module. + * The CAM_CLK_CTRL *is* really the right place. + */ + omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, + ULPD_CAM_CLK_CTRL); + + for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) { + pdev = omap16xx_gpio_dev[i]; + pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "Invalid mem resource.\n"); + return -ENODEV; + } + + base = ioremap(res->start, resource_size(res)); + if (unlikely(!base)) { + dev_err(&pdev->dev, "ioremap failed.\n"); + return -ENOMEM; + } + + __raw_writel(SYSCONFIG_WORD, base + OMAP1610_GPIO_SYSCONFIG); + iounmap(base); + + platform_device_register(omap16xx_gpio_dev[i]); + } + + return 0; +} +postcore_initcall(omap16xx_gpio_init); diff --git a/arch/arm/mach-omap1/gpio7xx.c b/arch/arm/mach-omap1/gpio7xx.c new file mode 100644 index 000000000..c372b357e --- /dev/null +++ b/arch/arm/mach-omap1/gpio7xx.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP7xx specific gpio init + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: + * Charulatha V <charu@ti.com> + */ + +#include <linux/gpio.h> +#include <linux/platform_data/gpio-omap.h> + +#include "irqs.h" +#include "soc.h" + +#define OMAP7XX_GPIO1_BASE 0xfffbc000 +#define OMAP7XX_GPIO2_BASE 0xfffbc800 +#define OMAP7XX_GPIO3_BASE 0xfffbd000 +#define OMAP7XX_GPIO4_BASE 0xfffbd800 +#define OMAP7XX_GPIO5_BASE 0xfffbe000 +#define OMAP7XX_GPIO6_BASE 0xfffbe800 +#define OMAP1_MPUIO_VBASE OMAP1_MPUIO_BASE + +/* mpu gpio */ +static struct resource omap7xx_mpu_gpio_resources[] = { + { + .start = OMAP1_MPUIO_VBASE, + .end = OMAP1_MPUIO_VBASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_MPUIO, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap7xx_mpuio_regs = { + .revision = USHRT_MAX, + .direction = OMAP_MPUIO_IO_CNTL / 2, + .datain = OMAP_MPUIO_INPUT_LATCH / 2, + .dataout = OMAP_MPUIO_OUTPUT / 2, + .irqstatus = OMAP_MPUIO_GPIO_INT / 2, + .irqenable = OMAP_MPUIO_GPIO_MASKIT / 2, + .irqenable_inv = true, + .irqctrl = OMAP_MPUIO_GPIO_INT_EDGE >> 1, +}; + +static struct omap_gpio_platform_data omap7xx_mpu_gpio_config = { + .is_mpuio = true, + .bank_width = 16, + .bank_stride = 2, + .regs = &omap7xx_mpuio_regs, +}; + +static struct platform_device omap7xx_mpu_gpio = { + .name = "omap_gpio", + .id = 0, + .dev = { + .platform_data = &omap7xx_mpu_gpio_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_mpu_gpio_resources), + .resource = omap7xx_mpu_gpio_resources, +}; + +/* gpio1 */ +static struct resource omap7xx_gpio1_resources[] = { + { + .start = OMAP7XX_GPIO1_BASE, + .end = OMAP7XX_GPIO1_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_reg_offs omap7xx_gpio_regs = { + .revision = USHRT_MAX, + .direction = OMAP7XX_GPIO_DIR_CONTROL, + .datain = OMAP7XX_GPIO_DATA_INPUT, + .dataout = OMAP7XX_GPIO_DATA_OUTPUT, + .irqstatus = OMAP7XX_GPIO_INT_STATUS, + .irqenable = OMAP7XX_GPIO_INT_MASK, + .irqenable_inv = true, + .irqctrl = OMAP7XX_GPIO_INT_CONTROL, +}; + +static struct omap_gpio_platform_data omap7xx_gpio1_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio1 = { + .name = "omap_gpio", + .id = 1, + .dev = { + .platform_data = &omap7xx_gpio1_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio1_resources), + .resource = omap7xx_gpio1_resources, +}; + +/* gpio2 */ +static struct resource omap7xx_gpio2_resources[] = { + { + .start = OMAP7XX_GPIO2_BASE, + .end = OMAP7XX_GPIO2_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap7xx_gpio2_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio2 = { + .name = "omap_gpio", + .id = 2, + .dev = { + .platform_data = &omap7xx_gpio2_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio2_resources), + .resource = omap7xx_gpio2_resources, +}; + +/* gpio3 */ +static struct resource omap7xx_gpio3_resources[] = { + { + .start = OMAP7XX_GPIO3_BASE, + .end = OMAP7XX_GPIO3_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap7xx_gpio3_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio3 = { + .name = "omap_gpio", + .id = 3, + .dev = { + .platform_data = &omap7xx_gpio3_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio3_resources), + .resource = omap7xx_gpio3_resources, +}; + +/* gpio4 */ +static struct resource omap7xx_gpio4_resources[] = { + { + .start = OMAP7XX_GPIO4_BASE, + .end = OMAP7XX_GPIO4_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK4, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap7xx_gpio4_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio4 = { + .name = "omap_gpio", + .id = 4, + .dev = { + .platform_data = &omap7xx_gpio4_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio4_resources), + .resource = omap7xx_gpio4_resources, +}; + +/* gpio5 */ +static struct resource omap7xx_gpio5_resources[] = { + { + .start = OMAP7XX_GPIO5_BASE, + .end = OMAP7XX_GPIO5_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK5, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap7xx_gpio5_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio5 = { + .name = "omap_gpio", + .id = 5, + .dev = { + .platform_data = &omap7xx_gpio5_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio5_resources), + .resource = omap7xx_gpio5_resources, +}; + +/* gpio6 */ +static struct resource omap7xx_gpio6_resources[] = { + { + .start = OMAP7XX_GPIO6_BASE, + .end = OMAP7XX_GPIO6_BASE + SZ_2K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_7XX_GPIO_BANK6, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_gpio_platform_data omap7xx_gpio6_config = { + .bank_width = 32, + .regs = &omap7xx_gpio_regs, +}; + +static struct platform_device omap7xx_gpio6 = { + .name = "omap_gpio", + .id = 6, + .dev = { + .platform_data = &omap7xx_gpio6_config, + }, + .num_resources = ARRAY_SIZE(omap7xx_gpio6_resources), + .resource = omap7xx_gpio6_resources, +}; + +static struct platform_device *omap7xx_gpio_dev[] __initdata = { + &omap7xx_mpu_gpio, + &omap7xx_gpio1, + &omap7xx_gpio2, + &omap7xx_gpio3, + &omap7xx_gpio4, + &omap7xx_gpio5, + &omap7xx_gpio6, +}; + +/* + * omap7xx_gpio_init needs to be done before + * machine_init functions access gpio APIs. + * Hence omap7xx_gpio_init is a postcore_initcall. + */ +static int __init omap7xx_gpio_init(void) +{ + int i; + + if (!cpu_is_omap7xx()) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(omap7xx_gpio_dev); i++) + platform_device_register(omap7xx_gpio_dev[i]); + + return 0; +} +postcore_initcall(omap7xx_gpio_init); diff --git a/arch/arm/mach-omap1/hardware.h b/arch/arm/mach-omap1/hardware.h new file mode 100644 index 000000000..c228234a1 --- /dev/null +++ b/arch/arm/mach-omap1/hardware.h @@ -0,0 +1,195 @@ +/* + * Hardware definitions for TI OMAP processors and boards + * + * NOTE: Please put device driver specific defines into a separate header + * file for each driver. + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. Greg Lonnon <glonnon@ridgerun.com> + * + * Reorganized for Linux-2.6 by Tony Lindgren <tony@atomide.com> + * and Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP_HARDWARE_H +#define __ASM_ARCH_OMAP_HARDWARE_H + +#include <linux/sizes.h> +#include <linux/soc/ti/omap1-io.h> +#ifndef __ASSEMBLER__ +#include <asm/types.h> +#include <linux/soc/ti/omap1-soc.h> + +#include "tc.h" + +/* Almost all documentation for chip and board memory maps assumes + * BM is clear. Most devel boards have a switch to control booting + * from NOR flash (using external chipselect 3) rather than mask ROM, + * which uses BM to interchange the physical CS0 and CS3 addresses. + */ +static inline u32 omap_cs0m_phys(void) +{ + return (omap_readl(EMIFS_CONFIG) & OMAP_EMIFS_CONFIG_BM) + ? OMAP_CS3_PHYS : 0; +} + +static inline u32 omap_cs3_phys(void) +{ + return (omap_readl(EMIFS_CONFIG) & OMAP_EMIFS_CONFIG_BM) + ? 0 : OMAP_CS3_PHYS; +} + +#endif /* ifndef __ASSEMBLER__ */ + +#define OMAP1_IO_OFFSET 0x00f00000 /* Virtual IO = 0xff0b0000 */ +#define OMAP1_IO_ADDRESS(pa) IOMEM((pa) - OMAP1_IO_OFFSET) + +#include "serial.h" + +/* + * --------------------------------------------------------------------------- + * Common definitions for all OMAP processors + * NOTE: Put all processor or board specific parts to the special header + * files. + * --------------------------------------------------------------------------- + */ + +/* + * ---------------------------------------------------------------------------- + * Timers + * ---------------------------------------------------------------------------- + */ +#define OMAP_MPU_TIMER1_BASE (0xfffec500) +#define OMAP_MPU_TIMER2_BASE (0xfffec600) +#define OMAP_MPU_TIMER3_BASE (0xfffec700) +#define MPU_TIMER_FREE (1 << 6) +#define MPU_TIMER_CLOCK_ENABLE (1 << 5) +#define MPU_TIMER_AR (1 << 1) +#define MPU_TIMER_ST (1 << 0) + +/* + * --------------------------------------------------------------------------- + * Watchdog timer + * --------------------------------------------------------------------------- + */ + +/* Watchdog timer within the OMAP3.2 gigacell */ +#define OMAP_MPU_WATCHDOG_BASE (0xfffec800) +#define OMAP_WDT_TIMER (OMAP_MPU_WATCHDOG_BASE + 0x0) +#define OMAP_WDT_LOAD_TIM (OMAP_MPU_WATCHDOG_BASE + 0x4) +#define OMAP_WDT_READ_TIM (OMAP_MPU_WATCHDOG_BASE + 0x4) +#define OMAP_WDT_TIMER_MODE (OMAP_MPU_WATCHDOG_BASE + 0x8) + +/* + * --------------------------------------------------------------------------- + * Interrupts + * --------------------------------------------------------------------------- + */ +#ifdef CONFIG_ARCH_OMAP1 + +/* + * XXX: These probably want to be moved to arch/arm/mach-omap/omap1/irq.c + * or something similar.. -- PFM. + */ + +#define OMAP_IH1_BASE 0xfffecb00 +#define OMAP_IH2_BASE 0xfffe0000 + +#define OMAP_IH1_ITR (OMAP_IH1_BASE + 0x00) +#define OMAP_IH1_MIR (OMAP_IH1_BASE + 0x04) +#define OMAP_IH1_SIR_IRQ (OMAP_IH1_BASE + 0x10) +#define OMAP_IH1_SIR_FIQ (OMAP_IH1_BASE + 0x14) +#define OMAP_IH1_CONTROL (OMAP_IH1_BASE + 0x18) +#define OMAP_IH1_ILR0 (OMAP_IH1_BASE + 0x1c) +#define OMAP_IH1_ISR (OMAP_IH1_BASE + 0x9c) + +#define OMAP_IH2_ITR (OMAP_IH2_BASE + 0x00) +#define OMAP_IH2_MIR (OMAP_IH2_BASE + 0x04) +#define OMAP_IH2_SIR_IRQ (OMAP_IH2_BASE + 0x10) +#define OMAP_IH2_SIR_FIQ (OMAP_IH2_BASE + 0x14) +#define OMAP_IH2_CONTROL (OMAP_IH2_BASE + 0x18) +#define OMAP_IH2_ILR0 (OMAP_IH2_BASE + 0x1c) +#define OMAP_IH2_ISR (OMAP_IH2_BASE + 0x9c) + +#define IRQ_ITR_REG_OFFSET 0x00 +#define IRQ_MIR_REG_OFFSET 0x04 +#define IRQ_SIR_IRQ_REG_OFFSET 0x10 +#define IRQ_SIR_FIQ_REG_OFFSET 0x14 +#define IRQ_CONTROL_REG_OFFSET 0x18 +#define IRQ_ISR_REG_OFFSET 0x9c +#define IRQ_ILR0_REG_OFFSET 0x1c +#define IRQ_GMR_REG_OFFSET 0xa0 + +#endif + +/* Timer32K for 1610 and 1710*/ +#define OMAP_TIMER32K_BASE 0xFFFBC400 + +/* + * --------------------------------------------------------------------------- + * TIPB bus interface + * --------------------------------------------------------------------------- + */ +#define TIPB_PUBLIC_CNTL_BASE 0xfffed300 +#define MPU_PUBLIC_TIPB_CNTL (TIPB_PUBLIC_CNTL_BASE + 0x8) +#define TIPB_PRIVATE_CNTL_BASE 0xfffeca00 +#define MPU_PRIVATE_TIPB_CNTL (TIPB_PRIVATE_CNTL_BASE + 0x8) + +/* + * ---------------------------------------------------------------------------- + * MPUI interface + * ---------------------------------------------------------------------------- + */ +#define MPUI_BASE (0xfffec900) +#define MPUI_CTRL (MPUI_BASE + 0x0) +#define MPUI_DEBUG_ADDR (MPUI_BASE + 0x4) +#define MPUI_DEBUG_DATA (MPUI_BASE + 0x8) +#define MPUI_DEBUG_FLAG (MPUI_BASE + 0xc) +#define MPUI_STATUS_REG (MPUI_BASE + 0x10) +#define MPUI_DSP_STATUS (MPUI_BASE + 0x14) +#define MPUI_DSP_BOOT_CONFIG (MPUI_BASE + 0x18) +#define MPUI_DSP_API_CONFIG (MPUI_BASE + 0x1c) + +/* + * ---------------------------------------------------------------------------- + * LED Pulse Generator + * ---------------------------------------------------------------------------- + */ +#define OMAP_LPG1_BASE 0xfffbd000 +#define OMAP_LPG2_BASE 0xfffbd800 +#define OMAP_LPG1_LCR (OMAP_LPG1_BASE + 0x00) +#define OMAP_LPG1_PMR (OMAP_LPG1_BASE + 0x04) +#define OMAP_LPG2_LCR (OMAP_LPG2_BASE + 0x00) +#define OMAP_LPG2_PMR (OMAP_LPG2_BASE + 0x04) + +/* + * --------------------------------------------------------------------------- + * Processor specific defines + * --------------------------------------------------------------------------- + */ + +#include "omap7xx.h" +#include "omap1510.h" +#include "omap16xx.h" + +#endif /* __ASM_ARCH_OMAP_HARDWARE_H */ diff --git a/arch/arm/mach-omap1/i2c.c b/arch/arm/mach-omap1/i2c.c new file mode 100644 index 000000000..22f945360 --- /dev/null +++ b/arch/arm/mach-omap1/i2c.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2009 Nokia Corporation. + */ + +#include <linux/i2c.h> +#include <linux/platform_data/i2c-omap.h> + +#include "mux.h" +#include "soc.h" +#include "i2c.h" + +#define OMAP_I2C_SIZE 0x3f +#define OMAP1_I2C_BASE 0xfffb3800 + +static const char name[] = "omap_i2c"; + +static struct resource i2c_resources[2] = { +}; + +static struct platform_device omap_i2c_devices[1] = { +}; + +static void __init omap1_i2c_mux_pins(int bus_id) +{ + if (cpu_is_omap7xx()) { + omap_cfg_reg(I2C_7XX_SDA); + omap_cfg_reg(I2C_7XX_SCL); + } else { + omap_cfg_reg(I2C_SDA); + omap_cfg_reg(I2C_SCL); + } +} + +int __init omap_i2c_add_bus(struct omap_i2c_bus_platform_data *pdata, + int bus_id) +{ + struct platform_device *pdev; + struct resource *res; + + if (bus_id > 1) + return -EINVAL; + + omap1_i2c_mux_pins(bus_id); + + pdev = &omap_i2c_devices[bus_id - 1]; + pdev->id = bus_id; + pdev->name = name; + pdev->num_resources = ARRAY_SIZE(i2c_resources); + res = i2c_resources; + res[0].start = OMAP1_I2C_BASE; + res[0].end = res[0].start + OMAP_I2C_SIZE; + res[0].flags = IORESOURCE_MEM; + res[1].start = INT_I2C; + res[1].flags = IORESOURCE_IRQ; + pdev->resource = res; + + /* all OMAP1 have IP version 1 register set */ + pdata->rev = OMAP_I2C_IP_VERSION_1; + + /* all OMAP1 I2C are implemented like this */ + pdata->flags = OMAP_I2C_FLAG_NO_FIFO | + OMAP_I2C_FLAG_SIMPLE_CLOCK | + OMAP_I2C_FLAG_16BIT_DATA_REG | + OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK; + + /* how the cpu bus is wired up differs for 7xx only */ + + if (cpu_is_omap7xx()) + pdata->flags |= OMAP_I2C_FLAG_BUS_SHIFT_1; + else + pdata->flags |= OMAP_I2C_FLAG_BUS_SHIFT_2; + + pdev->dev.platform_data = pdata; + + return platform_device_register(pdev); +} + +#define OMAP_I2C_MAX_CONTROLLERS 4 +static struct omap_i2c_bus_platform_data i2c_pdata[OMAP_I2C_MAX_CONTROLLERS]; + +#define OMAP_I2C_CMDLINE_SETUP (BIT(31)) + +/** + * omap_i2c_bus_setup - Process command line options for the I2C bus speed + * @str: String of options + * + * This function allow to override the default I2C bus speed for given I2C + * bus with a command line option. + * + * Format: i2c_bus=bus_id,clkrate (in kHz) + * + * Returns 1 on success, 0 otherwise. + */ +static int __init omap_i2c_bus_setup(char *str) +{ + int ints[3]; + + get_options(str, 3, ints); + if (ints[0] < 2 || ints[1] < 1 || + ints[1] > OMAP_I2C_MAX_CONTROLLERS) + return 0; + i2c_pdata[ints[1] - 1].clkrate = ints[2]; + i2c_pdata[ints[1] - 1].clkrate |= OMAP_I2C_CMDLINE_SETUP; + + return 1; +} +__setup("i2c_bus=", omap_i2c_bus_setup); + +/* + * Register busses defined in command line but that are not registered with + * omap_register_i2c_bus from board initialization code. + */ +int __init omap_register_i2c_bus_cmdline(void) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(i2c_pdata); i++) + if (i2c_pdata[i].clkrate & OMAP_I2C_CMDLINE_SETUP) { + i2c_pdata[i].clkrate &= ~OMAP_I2C_CMDLINE_SETUP; + err = omap_i2c_add_bus(&i2c_pdata[i], i + 1); + if (err) + goto out; + } + +out: + return err; +} + +/** + * omap_register_i2c_bus - register I2C bus with device descriptors + * @bus_id: bus id counting from number 1 + * @clkrate: clock rate of the bus in kHz + * @info: pointer into I2C device descriptor table or NULL + * @len: number of descriptors in the table + * + * Returns 0 on success or an error code. + */ +int __init omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len) +{ + int err; + + BUG_ON(bus_id < 1 || bus_id > OMAP_I2C_MAX_CONTROLLERS); + + if (info) { + err = i2c_register_board_info(bus_id, info, len); + if (err) + return err; + } + + if (!i2c_pdata[bus_id - 1].clkrate) + i2c_pdata[bus_id - 1].clkrate = clkrate; + + i2c_pdata[bus_id - 1].clkrate &= ~OMAP_I2C_CMDLINE_SETUP; + + return omap_i2c_add_bus(&i2c_pdata[bus_id - 1], bus_id); +} + +static int __init omap_i2c_cmdline(void) +{ + return omap_register_i2c_bus_cmdline(); +} +subsys_initcall(omap_i2c_cmdline); diff --git a/arch/arm/mach-omap1/i2c.h b/arch/arm/mach-omap1/i2c.h new file mode 100644 index 000000000..03e2ac2ef --- /dev/null +++ b/arch/arm/mach-omap1/i2c.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2009 Nokia Corporation. + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_I2C_H +#define __ARCH_ARM_MACH_OMAP1_I2C_H + +struct i2c_board_info; +struct omap_i2c_bus_platform_data; + +int omap_i2c_add_bus(struct omap_i2c_bus_platform_data *i2c_pdata, + int bus_id); + +#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) +extern int omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len); +extern int omap_register_i2c_bus_cmdline(void); +#else +static inline int omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len) +{ + return 0; +} + +static inline int omap_register_i2c_bus_cmdline(void) +{ + return 0; +} +#endif + +#endif /* __ARCH_ARM_MACH_OMAP1_I2C_H */ diff --git a/arch/arm/mach-omap1/id.c b/arch/arm/mach-omap1/id.c new file mode 100644 index 000000000..c3bb1b71f --- /dev/null +++ b/arch/arm/mach-omap1/id.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/id.c + * + * OMAP1 CPU identification code + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/soc/ti/omap1-io.h> +#include <asm/system_info.h> + +#include "soc.h" +#include "hardware.h" +#include "common.h" + +#define OMAP_DIE_ID_0 0xfffe1800 +#define OMAP_DIE_ID_1 0xfffe1804 +#define OMAP_PRODUCTION_ID_0 0xfffe2000 +#define OMAP_PRODUCTION_ID_1 0xfffe2004 +#define OMAP32_ID_0 0xfffed400 +#define OMAP32_ID_1 0xfffed404 + +struct omap_id { + u16 jtag_id; /* Used to determine OMAP type */ + u8 die_rev; /* Processor revision */ + u32 omap_id; /* OMAP revision */ + u32 type; /* Cpu id bits [31:08], cpu class bits [07:00] */ +}; + +static unsigned int omap_revision; + +/* Register values to detect the OMAP version */ +static struct omap_id omap_ids[] __initdata = { + { .jtag_id = 0xb574, .die_rev = 0x2, .omap_id = 0x03310315, .type = 0x03100000}, + { .jtag_id = 0x355f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300100}, + { .jtag_id = 0xb55f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300300}, + { .jtag_id = 0xb62c, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x08500000}, + { .jtag_id = 0xb470, .die_rev = 0x0, .omap_id = 0x03310100, .type = 0x15100000}, + { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x16100000}, + { .jtag_id = 0xb576, .die_rev = 0x2, .omap_id = 0x03320100, .type = 0x16110000}, + { .jtag_id = 0xb576, .die_rev = 0x3, .omap_id = 0x03320100, .type = 0x16100c00}, + { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320200, .type = 0x16100d00}, + { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, + { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, + { .jtag_id = 0xb576, .die_rev = 0x1, .omap_id = 0x03320100, .type = 0x16110000}, + { .jtag_id = 0xb58c, .die_rev = 0x2, .omap_id = 0x03320200, .type = 0x16110b00}, + { .jtag_id = 0xb58c, .die_rev = 0x3, .omap_id = 0x03320200, .type = 0x16110c00}, + { .jtag_id = 0xb65f, .die_rev = 0x0, .omap_id = 0x03320400, .type = 0x16212300}, + { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320400, .type = 0x16212300}, + { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x16212300}, + { .jtag_id = 0xb5f7, .die_rev = 0x0, .omap_id = 0x03330000, .type = 0x17100000}, + { .jtag_id = 0xb5f7, .die_rev = 0x1, .omap_id = 0x03330100, .type = 0x17100000}, + { .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000}, +}; + +unsigned int omap_rev(void) +{ + return omap_revision; +} +EXPORT_SYMBOL(omap_rev); + +/* + * Get OMAP type from PROD_ID. + * 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM. + * 1510 PROD_ID is empty, and 1610 PROD_ID does not make sense. + * Undocumented register in TEST BLOCK is used as fallback; This seems to + * work on 1510, 1610 & 1710. The official way hopefully will work in future + * processors. + */ +static u16 __init omap_get_jtag_id(void) +{ + u32 prod_id, omap_id; + + prod_id = omap_readl(OMAP_PRODUCTION_ID_1); + omap_id = omap_readl(OMAP32_ID_1); + + /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730/850 */ + if (((prod_id >> 20) == 0) || (prod_id == omap_id)) + prod_id = 0; + else + prod_id &= 0xffff; + + if (prod_id) + return prod_id; + + /* Use OMAP32_ID_1 as fallback */ + prod_id = ((omap_id >> 12) & 0xffff); + + return prod_id; +} + +/* + * Get OMAP revision from DIE_REV. + * Early 1710 processors may have broken OMAP_DIE_ID, it contains PROD_ID. + * Undocumented register in the TEST BLOCK is used as fallback. + * REVISIT: This does not seem to work on 1510 + */ +static u8 __init omap_get_die_rev(void) +{ + u32 die_rev; + + die_rev = omap_readl(OMAP_DIE_ID_1); + + /* Check for broken OMAP_DIE_ID on early 1710 */ + if (((die_rev >> 12) & 0xffff) == omap_get_jtag_id()) + die_rev = 0; + + die_rev = (die_rev >> 17) & 0xf; + if (die_rev) + return die_rev; + + die_rev = (omap_readl(OMAP32_ID_1) >> 28) & 0xf; + + return die_rev; +} + +void __init omap_check_revision(void) +{ + int i; + u16 jtag_id; + u8 die_rev; + u32 omap_id; + u8 cpu_type; + + jtag_id = omap_get_jtag_id(); + die_rev = omap_get_die_rev(); + omap_id = omap_readl(OMAP32_ID_0); + +#ifdef DEBUG + printk(KERN_DEBUG "OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0)); + printk(KERN_DEBUG "OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n", + omap_readl(OMAP_DIE_ID_1), + (omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf); + printk(KERN_DEBUG "OMAP_PRODUCTION_ID_0: 0x%08x\n", + omap_readl(OMAP_PRODUCTION_ID_0)); + printk(KERN_DEBUG "OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n", + omap_readl(OMAP_PRODUCTION_ID_1), + omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff); + printk(KERN_DEBUG "OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0)); + printk(KERN_DEBUG "OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1)); + printk(KERN_DEBUG "JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev); +#endif + + system_serial_high = omap_readl(OMAP_DIE_ID_0); + system_serial_low = omap_readl(OMAP_DIE_ID_1); + + /* First check only the major version in a safe way */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == (omap_ids[i].jtag_id)) { + omap_revision = omap_ids[i].type; + break; + } + } + + /* Check if we can find the die revision */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) { + omap_revision = omap_ids[i].type; + break; + } + } + + /* Finally check also the omap_id */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == omap_ids[i].jtag_id + && die_rev == omap_ids[i].die_rev + && omap_id == omap_ids[i].omap_id) { + omap_revision = omap_ids[i].type; + break; + } + } + + /* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */ + cpu_type = omap_revision >> 24; + + switch (cpu_type) { + case 0x07: + case 0x08: + omap_revision |= 0x07; + break; + case 0x03: + case 0x15: + omap_revision |= 0x15; + break; + case 0x16: + case 0x17: + omap_revision |= 0x16; + break; + default: + printk(KERN_INFO "Unknown OMAP cpu type: 0x%02x\n", cpu_type); + } + + pr_info("OMAP%04x", omap_revision >> 16); + if ((omap_revision >> 8) & 0xff) + pr_cont("%x", (omap_revision >> 8) & 0xff); + pr_cont(" revision %i handled as %02xxx id: %08x%08x\n", + die_rev, omap_revision & 0xff, system_serial_low, + system_serial_high); +} + diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c new file mode 100644 index 000000000..0074b011a --- /dev/null +++ b/arch/arm/mach-omap1/io.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/io.c + * + * OMAP1 I/O mapping code + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/omap-dma.h> + +#include <asm/tlb.h> +#include <asm/mach/map.h> + +#include "tc.h" +#include "iomap.h" +#include "common.h" + +/* + * The machine specific code may provide the extra mapping besides the + * default mapping provided here. + */ +#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850) +static struct map_desc omap7xx_io_desc[] __initdata = { + { + .virtual = OMAP1_IO_VIRT, + .pfn = __phys_to_pfn(OMAP1_IO_PHYS), + .length = OMAP1_IO_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP7XX_DSP_BASE, + .pfn = __phys_to_pfn(OMAP7XX_DSP_START), + .length = OMAP7XX_DSP_SIZE, + .type = MT_DEVICE + }, { + .virtual = OMAP7XX_DSPREG_BASE, + .pfn = __phys_to_pfn(OMAP7XX_DSPREG_START), + .length = OMAP7XX_DSPREG_SIZE, + .type = MT_DEVICE + } +}; +#endif + +#ifdef CONFIG_ARCH_OMAP15XX +static struct map_desc omap1510_io_desc[] __initdata = { + { + .virtual = OMAP1_IO_VIRT, + .pfn = __phys_to_pfn(OMAP1_IO_PHYS), + .length = OMAP1_IO_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP1510_DSP_BASE, + .pfn = __phys_to_pfn(OMAP1510_DSP_START), + .length = OMAP1510_DSP_SIZE, + .type = MT_DEVICE + }, { + .virtual = OMAP1510_DSPREG_BASE, + .pfn = __phys_to_pfn(OMAP1510_DSPREG_START), + .length = OMAP1510_DSPREG_SIZE, + .type = MT_DEVICE + } +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct map_desc omap16xx_io_desc[] __initdata = { + { + .virtual = OMAP1_IO_VIRT, + .pfn = __phys_to_pfn(OMAP1_IO_PHYS), + .length = OMAP1_IO_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP16XX_DSP_BASE, + .pfn = __phys_to_pfn(OMAP16XX_DSP_START), + .length = OMAP16XX_DSP_SIZE, + .type = MT_DEVICE + }, { + .virtual = OMAP16XX_DSPREG_BASE, + .pfn = __phys_to_pfn(OMAP16XX_DSPREG_START), + .length = OMAP16XX_DSPREG_SIZE, + .type = MT_DEVICE + } +}; +#endif + +#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850) +void __init omap7xx_map_io(void) +{ + iotable_init(omap7xx_io_desc, ARRAY_SIZE(omap7xx_io_desc)); +} +#endif + +#ifdef CONFIG_ARCH_OMAP15XX +void __init omap15xx_map_io(void) +{ + iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); +} +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +void __init omap16xx_map_io(void) +{ + iotable_init(omap16xx_io_desc, ARRAY_SIZE(omap16xx_io_desc)); +} +#endif + +/* + * Common low-level hardware init for omap1. + */ +void __init omap1_init_early(void) +{ + omap_check_revision(); + + /* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort + * on a Posted Write in the TIPB Bridge". + */ + omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL); + omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL); +} + +void __init omap1_init_late(void) +{ + omap_serial_wakeup_init(); +} + +/* + * NOTE: Please use ioremap + __raw_read/write where possible instead of these + */ + +u8 omap_readb(u32 pa) +{ + return __raw_readb(OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_readb); + +u16 omap_readw(u32 pa) +{ + return __raw_readw(OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_readw); + +u32 omap_readl(u32 pa) +{ + return __raw_readl(OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_readl); + +void omap_writeb(u8 v, u32 pa) +{ + __raw_writeb(v, OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_writeb); + +void omap_writew(u16 v, u32 pa) +{ + __raw_writew(v, OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_writew); + +void omap_writel(u32 v, u32 pa) +{ + __raw_writel(v, OMAP1_IO_ADDRESS(pa)); +} +EXPORT_SYMBOL(omap_writel); diff --git a/arch/arm/mach-omap1/iomap.h b/arch/arm/mach-omap1/iomap.h new file mode 100644 index 000000000..f4e2d7a21 --- /dev/null +++ b/arch/arm/mach-omap1/iomap.h @@ -0,0 +1,33 @@ +/* + * IO mappings for OMAP1 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * ---------------------------------------------------------------------------- + * Omap1 specific IO mapping + * ---------------------------------------------------------------------------- + */ + +#define OMAP1_IO_PHYS 0xFFFB0000 +#define OMAP1_IO_SIZE 0x40000 +#define OMAP1_IO_VIRT (OMAP1_IO_PHYS - OMAP1_IO_OFFSET) diff --git a/arch/arm/mach-omap1/irq.c b/arch/arm/mach-omap1/irq.c new file mode 100644 index 000000000..70868e9f1 --- /dev/null +++ b/arch/arm/mach-omap1/irq.c @@ -0,0 +1,274 @@ +/* + * linux/arch/arm/mach-omap1/irq.c + * + * Interrupt handler for all OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + * Major cleanups by Juha Yrjölä <juha.yrjola@nokia.com> + * + * Completely re-written to support various OMAP chips with bank specific + * interrupt handlers. + * + * Some snippets of the code taken from the older OMAP interrupt handler + * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon <glonnon@ridgerun.com> + * + * GPIO interrupt handler moved to gpio.c by Juha Yrjola + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#include "soc.h" +#include "hardware.h" +#include "common.h" + +#define IRQ_BANK(irq) ((irq) >> 5) +#define IRQ_BIT(irq) ((irq) & 0x1f) + +struct omap_irq_bank { + unsigned long base_reg; + void __iomem *va; + unsigned long trigger_map; + unsigned long wake_enable; +}; + +static u32 omap_l2_irq; +static unsigned int irq_bank_count; +static struct omap_irq_bank *irq_banks; +static struct irq_domain *domain; + +static inline unsigned int irq_bank_readl(int bank, int offset) +{ + return readl_relaxed(irq_banks[bank].va + offset); +} +static inline void irq_bank_writel(unsigned long value, int bank, int offset) +{ + writel_relaxed(value, irq_banks[bank].va + offset); +} + +static void omap_ack_irq(int irq) +{ + if (irq > 31) + writel_relaxed(0x1, irq_banks[1].va + IRQ_CONTROL_REG_OFFSET); + + writel_relaxed(0x1, irq_banks[0].va + IRQ_CONTROL_REG_OFFSET); +} + +static void omap_mask_ack_irq(struct irq_data *d) +{ + struct irq_chip_type *ct = irq_data_get_chip_type(d); + + ct->chip.irq_mask(d); + omap_ack_irq(d->irq); +} + +/* + * Allows tuning the IRQ type and priority + * + * NOTE: There is currently no OMAP fiq handler for Linux. Read the + * mailing list threads on FIQ handlers if you are planning to + * add a FIQ handler for OMAP. + */ +static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) +{ + signed int bank; + unsigned long val, offset; + + bank = IRQ_BANK(irq); + /* FIQ is only available on bank 0 interrupts */ + fiq = bank ? 0 : (fiq & 0x1); + val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1); + offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4; + irq_bank_writel(val, bank, offset); +} + +#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850) +static struct omap_irq_bank omap7xx_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, + { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP15XX +static struct omap_irq_bank omap1510_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, +}; +static struct omap_irq_bank omap310_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) + +static struct omap_irq_bank omap1610_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, + { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xffffb7ff }, + { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, +}; +#endif + +asmlinkage void __exception_irq_entry omap1_handle_irq(struct pt_regs *regs) +{ + void __iomem *l1 = irq_banks[0].va; + void __iomem *l2 = irq_banks[1].va; + u32 irqnr; + + do { + irqnr = readl_relaxed(l1 + IRQ_ITR_REG_OFFSET); + irqnr &= ~(readl_relaxed(l1 + IRQ_MIR_REG_OFFSET) & 0xffffffff); + if (!irqnr) + break; + + irqnr = readl_relaxed(l1 + IRQ_SIR_FIQ_REG_OFFSET); + if (irqnr) + goto irq; + + irqnr = readl_relaxed(l1 + IRQ_SIR_IRQ_REG_OFFSET); + if (irqnr == omap_l2_irq) { + irqnr = readl_relaxed(l2 + IRQ_SIR_IRQ_REG_OFFSET); + if (irqnr) + irqnr += 32; + } +irq: + if (irqnr) + generic_handle_domain_irq(domain, irqnr); + else + break; + } while (irqnr); +} + +static __init void +omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("MPU", 1, irq_start, base, + handle_level_irq); + ct = gc->chip_types; + ct->chip.irq_ack = omap_mask_ack_irq; + ct->chip.irq_mask = irq_gc_mask_set_bit; + ct->chip.irq_unmask = irq_gc_mask_clr_bit; + ct->chip.irq_set_wake = irq_gc_set_wake; + ct->regs.mask = IRQ_MIR_REG_OFFSET; + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); +} + +void __init omap1_init_irq(void) +{ + struct irq_chip_type *ct; + struct irq_data *d = NULL; + int i, j, irq_base; + unsigned long nr_irqs; + +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + if (cpu_is_omap7xx()) { + irq_banks = omap7xx_irq_banks; + irq_bank_count = ARRAY_SIZE(omap7xx_irq_banks); + } +#endif +#ifdef CONFIG_ARCH_OMAP15XX + if (cpu_is_omap1510()) { + irq_banks = omap1510_irq_banks; + irq_bank_count = ARRAY_SIZE(omap1510_irq_banks); + } + if (cpu_is_omap310()) { + irq_banks = omap310_irq_banks; + irq_bank_count = ARRAY_SIZE(omap310_irq_banks); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap16xx()) { + irq_banks = omap1610_irq_banks; + irq_bank_count = ARRAY_SIZE(omap1610_irq_banks); + } +#endif + + for (i = 0; i < irq_bank_count; i++) { + irq_banks[i].va = ioremap(irq_banks[i].base_reg, 0xff); + if (WARN_ON(!irq_banks[i].va)) + return; + } + + nr_irqs = irq_bank_count * 32; + + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (irq_base < 0) { + pr_warn("Couldn't allocate IRQ numbers\n"); + irq_base = 0; + } + omap_l2_irq = cpu_is_omap7xx() ? irq_base + 1 : irq_base; + omap_l2_irq -= NR_IRQS_LEGACY; + + domain = irq_domain_add_legacy(NULL, nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + + pr_info("Total of %lu interrupts in %i interrupt banks\n", + nr_irqs, irq_bank_count); + + /* Mask and clear all interrupts */ + for (i = 0; i < irq_bank_count; i++) { + irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET); + irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET); + } + + /* Clear any pending interrupts */ + irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET); + irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET); + + /* Enable interrupts in global mask */ + if (cpu_is_omap7xx()) + irq_bank_writel(0x0, 0, IRQ_GMR_REG_OFFSET); + + /* Install the interrupt handlers for each bank */ + for (i = 0; i < irq_bank_count; i++) { + for (j = i * 32; j < (i + 1) * 32; j++) { + int irq_trigger; + + irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j); + omap_irq_set_cfg(j, 0, 0, irq_trigger); + irq_clear_status_flags(j, IRQ_NOREQUEST); + } + omap_alloc_gc(irq_banks[i].va, irq_base + i * 32, 32); + } + + /* Unmask level 2 handler */ + d = irq_get_irq_data(irq_find_mapping(domain, omap_l2_irq)); + if (d) { + ct = irq_data_get_chip_type(d); + ct->chip.irq_unmask(d); + } +} diff --git a/arch/arm/mach-omap1/irqs.h b/arch/arm/mach-omap1/irqs.h new file mode 100644 index 000000000..2851acfe5 --- /dev/null +++ b/arch/arm/mach-omap1/irqs.h @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) Greg Lonnon 2001 + * Updated for OMAP-1610 by Tony Lindgren <tony@atomide.com> + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * NOTE: The interrupt vectors for the OMAP-1509, OMAP-1510, and OMAP-1610 + * are different. + */ + +#ifndef __ASM_ARCH_OMAP15XX_IRQS_H +#define __ASM_ARCH_OMAP15XX_IRQS_H + +/* + * IRQ numbers for interrupt handler 1 + * + * NOTE: See also the OMAP-1510 and 1610 specific IRQ numbers below + * + */ +#define INT_CAMERA (NR_IRQS_LEGACY + 1) +#define INT_FIQ (NR_IRQS_LEGACY + 3) +#define INT_RTDX (NR_IRQS_LEGACY + 6) +#define INT_DSP_MMU_ABORT (NR_IRQS_LEGACY + 7) +#define INT_HOST (NR_IRQS_LEGACY + 8) +#define INT_ABORT (NR_IRQS_LEGACY + 9) +#define INT_BRIDGE_PRIV (NR_IRQS_LEGACY + 13) +#define INT_GPIO_BANK1 (NR_IRQS_LEGACY + 14) +#define INT_UART3 (NR_IRQS_LEGACY + 15) +#define INT_TIMER3 (NR_IRQS_LEGACY + 16) +#define INT_DMA_CH0_6 (NR_IRQS_LEGACY + 19) +#define INT_DMA_CH1_7 (NR_IRQS_LEGACY + 20) +#define INT_DMA_CH2_8 (NR_IRQS_LEGACY + 21) +#define INT_DMA_CH3 (NR_IRQS_LEGACY + 22) +#define INT_DMA_CH4 (NR_IRQS_LEGACY + 23) +#define INT_DMA_CH5 (NR_IRQS_LEGACY + 24) +#define INT_TIMER1 (NR_IRQS_LEGACY + 26) +#define INT_WD_TIMER (NR_IRQS_LEGACY + 27) +#define INT_BRIDGE_PUB (NR_IRQS_LEGACY + 28) +#define INT_TIMER2 (NR_IRQS_LEGACY + 30) +#define INT_LCD_CTRL (NR_IRQS_LEGACY + 31) + +/* + * OMAP-1510 specific IRQ numbers for interrupt handler 1 + */ +#define INT_1510_IH2_IRQ (NR_IRQS_LEGACY + 0) +#define INT_1510_RES2 (NR_IRQS_LEGACY + 2) +#define INT_1510_SPI_TX (NR_IRQS_LEGACY + 4) +#define INT_1510_SPI_RX (NR_IRQS_LEGACY + 5) +#define INT_1510_DSP_MAILBOX1 (NR_IRQS_LEGACY + 10) +#define INT_1510_DSP_MAILBOX2 (NR_IRQS_LEGACY + 11) +#define INT_1510_RES12 (NR_IRQS_LEGACY + 12) +#define INT_1510_LB_MMU (NR_IRQS_LEGACY + 17) +#define INT_1510_RES18 (NR_IRQS_LEGACY + 18) +#define INT_1510_LOCAL_BUS (NR_IRQS_LEGACY + 29) + +/* + * OMAP-1610 specific IRQ numbers for interrupt handler 1 + */ +#define INT_1610_IH2_IRQ INT_1510_IH2_IRQ +#define INT_1610_IH2_FIQ (NR_IRQS_LEGACY + 2) +#define INT_1610_McBSP2_TX (NR_IRQS_LEGACY + 4) +#define INT_1610_McBSP2_RX (NR_IRQS_LEGACY + 5) +#define INT_1610_DSP_MAILBOX1 (NR_IRQS_LEGACY + 10) +#define INT_1610_DSP_MAILBOX2 (NR_IRQS_LEGACY + 11) +#define INT_1610_LCD_LINE (NR_IRQS_LEGACY + 12) +#define INT_1610_GPTIMER1 (NR_IRQS_LEGACY + 17) +#define INT_1610_GPTIMER2 (NR_IRQS_LEGACY + 18) +#define INT_1610_SSR_FIFO_0 (NR_IRQS_LEGACY + 29) + +/* + * OMAP-7xx specific IRQ numbers for interrupt handler 1 + */ +#define INT_7XX_IH2_FIQ (NR_IRQS_LEGACY + 0) +#define INT_7XX_IH2_IRQ (NR_IRQS_LEGACY + 1) +#define INT_7XX_USB_NON_ISO (NR_IRQS_LEGACY + 2) +#define INT_7XX_USB_ISO (NR_IRQS_LEGACY + 3) +#define INT_7XX_ICR (NR_IRQS_LEGACY + 4) +#define INT_7XX_EAC (NR_IRQS_LEGACY + 5) +#define INT_7XX_GPIO_BANK1 (NR_IRQS_LEGACY + 6) +#define INT_7XX_GPIO_BANK2 (NR_IRQS_LEGACY + 7) +#define INT_7XX_GPIO_BANK3 (NR_IRQS_LEGACY + 8) +#define INT_7XX_McBSP2TX (NR_IRQS_LEGACY + 10) +#define INT_7XX_McBSP2RX (NR_IRQS_LEGACY + 11) +#define INT_7XX_McBSP2RX_OVF (NR_IRQS_LEGACY + 12) +#define INT_7XX_LCD_LINE (NR_IRQS_LEGACY + 14) +#define INT_7XX_GSM_PROTECT (NR_IRQS_LEGACY + 15) +#define INT_7XX_TIMER3 (NR_IRQS_LEGACY + 16) +#define INT_7XX_GPIO_BANK5 (NR_IRQS_LEGACY + 17) +#define INT_7XX_GPIO_BANK6 (NR_IRQS_LEGACY + 18) +#define INT_7XX_SPGIO_WR (NR_IRQS_LEGACY + 29) + +/* + * IRQ numbers for interrupt handler 2 + * + * NOTE: See also the OMAP-1510 and 1610 specific IRQ numbers below + */ +#define IH2_BASE (NR_IRQS_LEGACY + 32) + +#define INT_KEYBOARD (1 + IH2_BASE) +#define INT_uWireTX (2 + IH2_BASE) +#define INT_uWireRX (3 + IH2_BASE) +#define INT_I2C (4 + IH2_BASE) +#define INT_MPUIO (5 + IH2_BASE) +#define INT_USB_HHC_1 (6 + IH2_BASE) +#define INT_McBSP3TX (10 + IH2_BASE) +#define INT_McBSP3RX (11 + IH2_BASE) +#define INT_McBSP1TX (12 + IH2_BASE) +#define INT_McBSP1RX (13 + IH2_BASE) +#define INT_UART1 (14 + IH2_BASE) +#define INT_UART2 (15 + IH2_BASE) +#define INT_BT_MCSI1TX (16 + IH2_BASE) +#define INT_BT_MCSI1RX (17 + IH2_BASE) +#define INT_SOSSI_MATCH (19 + IH2_BASE) +#define INT_USB_W2FC (20 + IH2_BASE) +#define INT_1WIRE (21 + IH2_BASE) +#define INT_OS_TIMER (22 + IH2_BASE) +#define INT_MMC (23 + IH2_BASE) +#define INT_GAUGE_32K (24 + IH2_BASE) +#define INT_RTC_TIMER (25 + IH2_BASE) +#define INT_RTC_ALARM (26 + IH2_BASE) +#define INT_MEM_STICK (27 + IH2_BASE) + +/* + * OMAP-1510 specific IRQ numbers for interrupt handler 2 + */ +#define INT_1510_DSP_MMU (28 + IH2_BASE) +#define INT_1510_COM_SPI_RO (31 + IH2_BASE) + +/* + * OMAP-1610 specific IRQ numbers for interrupt handler 2 + */ +#define INT_1610_FAC (0 + IH2_BASE) +#define INT_1610_USB_HHC_2 (7 + IH2_BASE) +#define INT_1610_USB_OTG (8 + IH2_BASE) +#define INT_1610_SoSSI (9 + IH2_BASE) +#define INT_1610_SoSSI_MATCH (19 + IH2_BASE) +#define INT_1610_DSP_MMU (28 + IH2_BASE) +#define INT_1610_McBSP2RX_OF (31 + IH2_BASE) +#define INT_1610_STI (32 + IH2_BASE) +#define INT_1610_STI_WAKEUP (33 + IH2_BASE) +#define INT_1610_GPTIMER3 (34 + IH2_BASE) +#define INT_1610_GPTIMER4 (35 + IH2_BASE) +#define INT_1610_GPTIMER5 (36 + IH2_BASE) +#define INT_1610_GPTIMER6 (37 + IH2_BASE) +#define INT_1610_GPTIMER7 (38 + IH2_BASE) +#define INT_1610_GPTIMER8 (39 + IH2_BASE) +#define INT_1610_GPIO_BANK2 (40 + IH2_BASE) +#define INT_1610_GPIO_BANK3 (41 + IH2_BASE) +#define INT_1610_MMC2 (42 + IH2_BASE) +#define INT_1610_CF (43 + IH2_BASE) +#define INT_1610_WAKE_UP_REQ (46 + IH2_BASE) +#define INT_1610_GPIO_BANK4 (48 + IH2_BASE) +#define INT_1610_SPI (49 + IH2_BASE) +#define INT_1610_DMA_CH6 (53 + IH2_BASE) +#define INT_1610_DMA_CH7 (54 + IH2_BASE) +#define INT_1610_DMA_CH8 (55 + IH2_BASE) +#define INT_1610_DMA_CH9 (56 + IH2_BASE) +#define INT_1610_DMA_CH10 (57 + IH2_BASE) +#define INT_1610_DMA_CH11 (58 + IH2_BASE) +#define INT_1610_DMA_CH12 (59 + IH2_BASE) +#define INT_1610_DMA_CH13 (60 + IH2_BASE) +#define INT_1610_DMA_CH14 (61 + IH2_BASE) +#define INT_1610_DMA_CH15 (62 + IH2_BASE) +#define INT_1610_NAND (63 + IH2_BASE) +#define INT_1610_SHA1MD5 (91 + IH2_BASE) + +/* + * OMAP-7xx specific IRQ numbers for interrupt handler 2 + */ +#define INT_7XX_HW_ERRORS (0 + IH2_BASE) +#define INT_7XX_NFIQ_PWR_FAIL (1 + IH2_BASE) +#define INT_7XX_CFCD (2 + IH2_BASE) +#define INT_7XX_CFIREQ (3 + IH2_BASE) +#define INT_7XX_I2C (4 + IH2_BASE) +#define INT_7XX_PCC (5 + IH2_BASE) +#define INT_7XX_MPU_EXT_NIRQ (6 + IH2_BASE) +#define INT_7XX_SPI_100K_1 (7 + IH2_BASE) +#define INT_7XX_SYREN_SPI (8 + IH2_BASE) +#define INT_7XX_VLYNQ (9 + IH2_BASE) +#define INT_7XX_GPIO_BANK4 (10 + IH2_BASE) +#define INT_7XX_McBSP1TX (11 + IH2_BASE) +#define INT_7XX_McBSP1RX (12 + IH2_BASE) +#define INT_7XX_McBSP1RX_OF (13 + IH2_BASE) +#define INT_7XX_UART_MODEM_IRDA_2 (14 + IH2_BASE) +#define INT_7XX_UART_MODEM_1 (15 + IH2_BASE) +#define INT_7XX_MCSI (16 + IH2_BASE) +#define INT_7XX_uWireTX (17 + IH2_BASE) +#define INT_7XX_uWireRX (18 + IH2_BASE) +#define INT_7XX_SMC_CD (19 + IH2_BASE) +#define INT_7XX_SMC_IREQ (20 + IH2_BASE) +#define INT_7XX_HDQ_1WIRE (21 + IH2_BASE) +#define INT_7XX_TIMER32K (22 + IH2_BASE) +#define INT_7XX_MMC_SDIO (23 + IH2_BASE) +#define INT_7XX_UPLD (24 + IH2_BASE) +#define INT_7XX_USB_HHC_1 (27 + IH2_BASE) +#define INT_7XX_USB_HHC_2 (28 + IH2_BASE) +#define INT_7XX_USB_GENI (29 + IH2_BASE) +#define INT_7XX_USB_OTG (30 + IH2_BASE) +#define INT_7XX_CAMERA_IF (31 + IH2_BASE) +#define INT_7XX_RNG (32 + IH2_BASE) +#define INT_7XX_DUAL_MODE_TIMER (33 + IH2_BASE) +#define INT_7XX_DBB_RF_EN (34 + IH2_BASE) +#define INT_7XX_MPUIO_KEYPAD (35 + IH2_BASE) +#define INT_7XX_SHA1_MD5 (36 + IH2_BASE) +#define INT_7XX_SPI_100K_2 (37 + IH2_BASE) +#define INT_7XX_RNG_IDLE (38 + IH2_BASE) +#define INT_7XX_MPUIO (39 + IH2_BASE) +#define INT_7XX_LLPC_LCD_CTRL_CAN_BE_OFF (40 + IH2_BASE) +#define INT_7XX_LLPC_OE_FALLING (41 + IH2_BASE) +#define INT_7XX_LLPC_OE_RISING (42 + IH2_BASE) +#define INT_7XX_LLPC_VSYNC (43 + IH2_BASE) +#define INT_7XX_WAKE_UP_REQ (46 + IH2_BASE) +#define INT_7XX_DMA_CH6 (53 + IH2_BASE) +#define INT_7XX_DMA_CH7 (54 + IH2_BASE) +#define INT_7XX_DMA_CH8 (55 + IH2_BASE) +#define INT_7XX_DMA_CH9 (56 + IH2_BASE) +#define INT_7XX_DMA_CH10 (57 + IH2_BASE) +#define INT_7XX_DMA_CH11 (58 + IH2_BASE) +#define INT_7XX_DMA_CH12 (59 + IH2_BASE) +#define INT_7XX_DMA_CH13 (60 + IH2_BASE) +#define INT_7XX_DMA_CH14 (61 + IH2_BASE) +#define INT_7XX_DMA_CH15 (62 + IH2_BASE) +#define INT_7XX_NAND (63 + IH2_BASE) + +/* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730/850) and + * 16 MPUIO lines */ +#define OMAP_MAX_GPIO_LINES 192 +#define IH_GPIO_BASE (128 + IH2_BASE) +#define IH_MPUIO_BASE (OMAP_MAX_GPIO_LINES + IH_GPIO_BASE) +#define OMAP_IRQ_END (IH_MPUIO_BASE + 16) + +/* External FPGA handles interrupts on Innovator boards */ +#define OMAP_FPGA_IRQ_BASE (OMAP_IRQ_END) +#ifdef CONFIG_MACH_OMAP_INNOVATOR +#define OMAP_FPGA_NR_IRQS 24 +#else +#define OMAP_FPGA_NR_IRQS 0 +#endif +#define OMAP_FPGA_IRQ_END (OMAP_FPGA_IRQ_BASE + OMAP_FPGA_NR_IRQS) + +#define OMAP_IRQ_BIT(irq) (1 << ((irq - NR_IRQS_LEGACY) % 32)) + +#ifdef CONFIG_FIQ +#define FIQ_START 1024 +#endif + +#endif diff --git a/arch/arm/mach-omap1/mcbsp.c b/arch/arm/mach-omap1/mcbsp.c new file mode 100644 index 000000000..b1632cbe3 --- /dev/null +++ b/arch/arm/mach-omap1/mcbsp.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/mcbsp.c + * + * Copyright (C) 2008 Instituto Nokia de Tecnologia + * Contact: Eduardo Valentin <eduardo.valentin@indt.org.br> + * + * Multichannel mode not supported. + */ +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/omap-dma.h> +#include <linux/soc/ti/omap1-io.h> +#include <linux/platform_data/asoc-ti-mcbsp.h> + +#include "mux.h" +#include "soc.h" +#include "irqs.h" +#include "iomap.h" + +#define DPS_RSTCT2_PER_EN (1 << 0) +#define DSP_RSTCT2_WD_PER_EN (1 << 1) + +static int dsp_use; +static struct clk *api_clk; +static struct clk *dsp_clk; +static struct platform_device **omap_mcbsp_devices; + +static void omap1_mcbsp_request(unsigned int id) +{ + /* + * On 1510, 1610 and 1710, McBSP1 and McBSP3 + * are DSP public peripherals. + */ + if (id == 0 || id == 2) { + if (dsp_use++ == 0) { + api_clk = clk_get(NULL, "api_ck"); + dsp_clk = clk_get(NULL, "dsp_ck"); + if (!IS_ERR(api_clk) && !IS_ERR(dsp_clk)) { + clk_prepare_enable(api_clk); + clk_prepare_enable(dsp_clk); + + /* + * DSP external peripheral reset + * FIXME: This should be moved to dsp code + */ + __raw_writew(__raw_readw(DSP_RSTCT2) | DPS_RSTCT2_PER_EN | + DSP_RSTCT2_WD_PER_EN, DSP_RSTCT2); + } + } + } +} + +static void omap1_mcbsp_free(unsigned int id) +{ + if (id == 0 || id == 2) { + if (--dsp_use == 0) { + if (!IS_ERR(api_clk)) { + clk_disable_unprepare(api_clk); + clk_put(api_clk); + } + if (!IS_ERR(dsp_clk)) { + clk_disable_unprepare(dsp_clk); + clk_put(dsp_clk); + } + } + } +} + +static struct omap_mcbsp_ops omap1_mcbsp_ops = { + .request = omap1_mcbsp_request, + .free = omap1_mcbsp_free, +}; + +#define OMAP7XX_MCBSP1_BASE 0xfffb1000 +#define OMAP7XX_MCBSP2_BASE 0xfffb1800 + +#define OMAP1510_MCBSP1_BASE 0xe1011800 +#define OMAP1510_MCBSP2_BASE 0xfffb1000 +#define OMAP1510_MCBSP3_BASE 0xe1017000 + +#define OMAP1610_MCBSP1_BASE 0xe1011800 +#define OMAP1610_MCBSP2_BASE 0xfffb1000 +#define OMAP1610_MCBSP3_BASE 0xe1017000 + +struct resource omap7xx_mcbsp_res[][6] = { + { + { + .start = OMAP7XX_MCBSP1_BASE, + .end = OMAP7XX_MCBSP1_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_7XX_McBSP1RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_7XX_McBSP1TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 9, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 8, + .flags = IORESOURCE_DMA, + }, + }, + { + { + .start = OMAP7XX_MCBSP2_BASE, + .end = OMAP7XX_MCBSP2_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_7XX_McBSP2RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_7XX_McBSP2TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 11, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 10, + .flags = IORESOURCE_DMA, + }, + }, +}; + +#define omap7xx_mcbsp_res_0 omap7xx_mcbsp_res[0] + +static struct omap_mcbsp_platform_data omap7xx_mcbsp_pdata[] = { + { + .ops = &omap1_mcbsp_ops, + }, + { + .ops = &omap1_mcbsp_ops, + }, +}; +#define OMAP7XX_MCBSP_RES_SZ ARRAY_SIZE(omap7xx_mcbsp_res[1]) +#define OMAP7XX_MCBSP_COUNT ARRAY_SIZE(omap7xx_mcbsp_res) + +struct resource omap15xx_mcbsp_res[][6] = { + { + { + .start = OMAP1510_MCBSP1_BASE, + .end = OMAP1510_MCBSP1_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_McBSP1RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_McBSP1TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 9, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 8, + .flags = IORESOURCE_DMA, + }, + }, + { + { + .start = OMAP1510_MCBSP2_BASE, + .end = OMAP1510_MCBSP2_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_1510_SPI_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_1510_SPI_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 17, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 16, + .flags = IORESOURCE_DMA, + }, + }, + { + { + .start = OMAP1510_MCBSP3_BASE, + .end = OMAP1510_MCBSP3_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_McBSP3RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_McBSP3TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 11, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 10, + .flags = IORESOURCE_DMA, + }, + }, +}; + +#define omap15xx_mcbsp_res_0 omap15xx_mcbsp_res[0] + +static struct omap_mcbsp_platform_data omap15xx_mcbsp_pdata[] = { + { + .ops = &omap1_mcbsp_ops, + }, + { + .ops = &omap1_mcbsp_ops, + }, + { + .ops = &omap1_mcbsp_ops, + }, +}; +#define OMAP15XX_MCBSP_RES_SZ ARRAY_SIZE(omap15xx_mcbsp_res[1]) +#define OMAP15XX_MCBSP_COUNT ARRAY_SIZE(omap15xx_mcbsp_res) + +struct resource omap16xx_mcbsp_res[][6] = { + { + { + .start = OMAP1610_MCBSP1_BASE, + .end = OMAP1610_MCBSP1_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_McBSP1RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_McBSP1TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 9, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 8, + .flags = IORESOURCE_DMA, + }, + }, + { + { + .start = OMAP1610_MCBSP2_BASE, + .end = OMAP1610_MCBSP2_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_1610_McBSP2_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_1610_McBSP2_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 17, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 16, + .flags = IORESOURCE_DMA, + }, + }, + { + { + .start = OMAP1610_MCBSP3_BASE, + .end = OMAP1610_MCBSP3_BASE + SZ_256, + .flags = IORESOURCE_MEM, + }, + { + .name = "rx", + .start = INT_McBSP3RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "tx", + .start = INT_McBSP3TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = 11, + .flags = IORESOURCE_DMA, + }, + { + .name = "tx", + .start = 10, + .flags = IORESOURCE_DMA, + }, + }, +}; + +#define omap16xx_mcbsp_res_0 omap16xx_mcbsp_res[0] + +static struct omap_mcbsp_platform_data omap16xx_mcbsp_pdata[] = { + { + .ops = &omap1_mcbsp_ops, + }, + { + .ops = &omap1_mcbsp_ops, + }, + { + .ops = &omap1_mcbsp_ops, + }, +}; +#define OMAP16XX_MCBSP_RES_SZ ARRAY_SIZE(omap16xx_mcbsp_res[1]) +#define OMAP16XX_MCBSP_COUNT ARRAY_SIZE(omap16xx_mcbsp_res) + +static void omap_mcbsp_register_board_cfg(struct resource *res, int res_count, + struct omap_mcbsp_platform_data *config, int size) +{ + int i; + + omap_mcbsp_devices = kcalloc(size, sizeof(struct platform_device *), + GFP_KERNEL); + if (!omap_mcbsp_devices) { + printk(KERN_ERR "Could not register McBSP devices\n"); + return; + } + + for (i = 0; i < size; i++) { + struct platform_device *new_mcbsp; + int ret; + + new_mcbsp = platform_device_alloc("omap-mcbsp", i + 1); + if (!new_mcbsp) + continue; + platform_device_add_resources(new_mcbsp, &res[i * res_count], + res_count); + config[i].reg_size = 2; + config[i].reg_step = 2; + new_mcbsp->dev.platform_data = &config[i]; + ret = platform_device_add(new_mcbsp); + if (ret) { + platform_device_put(new_mcbsp); + continue; + } + omap_mcbsp_devices[i] = new_mcbsp; + } +} + +static int __init omap1_mcbsp_init(void) +{ + if (!cpu_class_is_omap1()) + return -ENODEV; + + if (cpu_is_omap7xx()) + omap_mcbsp_register_board_cfg(omap7xx_mcbsp_res_0, + OMAP7XX_MCBSP_RES_SZ, + omap7xx_mcbsp_pdata, + OMAP7XX_MCBSP_COUNT); + + if (cpu_is_omap15xx()) + omap_mcbsp_register_board_cfg(omap15xx_mcbsp_res_0, + OMAP15XX_MCBSP_RES_SZ, + omap15xx_mcbsp_pdata, + OMAP15XX_MCBSP_COUNT); + + if (cpu_is_omap16xx()) + omap_mcbsp_register_board_cfg(omap16xx_mcbsp_res_0, + OMAP16XX_MCBSP_RES_SZ, + omap16xx_mcbsp_pdata, + OMAP16XX_MCBSP_COUNT); + + return 0; +} + +arch_initcall(omap1_mcbsp_init); diff --git a/arch/arm/mach-omap1/mmc.h b/arch/arm/mach-omap1/mmc.h new file mode 100644 index 000000000..043bd02f3 --- /dev/null +++ b/arch/arm/mach-omap1/mmc.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/mmc/host.h> +#include <linux/platform_data/mmc-omap.h> + +#define OMAP15XX_NR_MMC 1 +#define OMAP16XX_NR_MMC 2 +#define OMAP1_MMC_SIZE 0x080 +#define OMAP1_MMC1_BASE 0xfffb7800 +#define OMAP1_MMC2_BASE 0xfffb7c00 /* omap16xx only */ + +#if IS_ENABLED(CONFIG_MMC_OMAP) +void omap1_init_mmc(struct omap_mmc_platform_data **mmc_data, + int nr_controllers); +#else +static inline void omap1_init_mmc(struct omap_mmc_platform_data **mmc_data, + int nr_controllers) +{ +} +#endif diff --git a/arch/arm/mach-omap1/mtd-xip.h b/arch/arm/mach-omap1/mtd-xip.h new file mode 100644 index 000000000..5ae312ff0 --- /dev/null +++ b/arch/arm/mach-omap1/mtd-xip.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MTD primitives for XIP support. Architecture specific functions. + * + * Do not include this file directly. It's included from linux/mtd/xip.h + * + * Author: Vladimir Barinov <vbarinov@embeddedalley.com> + * + * (c) 2005 MontaVista Software, Inc. + */ + +#ifndef __ARCH_OMAP_MTD_XIP_H__ +#define __ARCH_OMAP_MTD_XIP_H__ + +#include "hardware.h" +#include <linux/soc/ti/omap1-io.h> +#define OMAP_MPU_TIMER_BASE (0xfffec500) +#define OMAP_MPU_TIMER_OFFSET 0x100 + +typedef struct { + u32 cntl; /* CNTL_TIMER, R/W */ + u32 load_tim; /* LOAD_TIM, W */ + u32 read_tim; /* READ_TIM, R */ +} xip_omap_mpu_timer_regs_t; + +#define xip_omap_mpu_timer_base(n) \ +((volatile xip_omap_mpu_timer_regs_t*)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ + (n)*OMAP_MPU_TIMER_OFFSET)) + +static inline unsigned long xip_omap_mpu_timer_read(int nr) +{ + volatile xip_omap_mpu_timer_regs_t* timer = xip_omap_mpu_timer_base(nr); + return timer->read_tim; +} + +#define xip_irqpending() \ + (omap_readl(OMAP_IH1_ITR) & ~omap_readl(OMAP_IH1_MIR)) +#define xip_currtime() (~xip_omap_mpu_timer_read(0)) + +/* + * It's permitted to do approximation for xip_elapsed_since macro + * (see linux/mtd/xip.h) + */ + +#ifdef CONFIG_MACH_OMAP_PERSEUS2 +#define xip_elapsed_since(x) (signed)((~xip_omap_mpu_timer_read(0) - (x)) / 7) +#else +#define xip_elapsed_since(x) (signed)((~xip_omap_mpu_timer_read(0) - (x)) / 6) +#endif + +/* + * xip_cpu_idle() is used when waiting for a delay equal or larger than + * the system timer tick period. This should put the CPU into idle mode + * to save power and to be woken up only when some interrupts are pending. + * As above, this should not rely upon standard kernel code. + */ + +#define xip_cpu_idle() asm volatile ("mcr p15, 0, %0, c7, c0, 4" :: "r" (1)) + +#endif /* __ARCH_OMAP_MTD_XIP_H__ */ diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c new file mode 100644 index 000000000..2d9458ff1 --- /dev/null +++ b/arch/arm/mach-omap1/mux.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/arch/arm/mach-omap1/mux.c + * + * OMAP1 pin multiplexing configurations + * + * Copyright (C) 2003 - 2008 Nokia Corporation + * + * Written by Tony Lindgren + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/soc/ti/omap1-io.h> + +#include "hardware.h" +#include "mux.h" + +#ifdef CONFIG_OMAP_MUX + +static struct omap_mux_cfg arch_mux_cfg; + +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) +static struct pin_config omap7xx_pins[] = { +MUX_CFG_7XX("E2_7XX_KBR0", 12, 21, 0, 20, 1, 0) +MUX_CFG_7XX("J7_7XX_KBR1", 12, 25, 0, 24, 1, 0) +MUX_CFG_7XX("E1_7XX_KBR2", 12, 29, 0, 28, 1, 0) +MUX_CFG_7XX("F3_7XX_KBR3", 13, 1, 0, 0, 1, 0) +MUX_CFG_7XX("D2_7XX_KBR4", 13, 5, 0, 4, 1, 0) +MUX_CFG_7XX("C2_7XX_KBC0", 13, 9, 0, 8, 1, 0) +MUX_CFG_7XX("D3_7XX_KBC1", 13, 13, 0, 12, 1, 0) +MUX_CFG_7XX("E4_7XX_KBC2", 13, 17, 0, 16, 1, 0) +MUX_CFG_7XX("F4_7XX_KBC3", 13, 21, 0, 20, 1, 0) +MUX_CFG_7XX("E3_7XX_KBC4", 13, 25, 0, 24, 1, 0) + +MUX_CFG_7XX("AA17_7XX_USB_DM", 2, 21, 0, 20, 0, 0) +MUX_CFG_7XX("W16_7XX_USB_PU_EN", 2, 25, 0, 24, 0, 0) +MUX_CFG_7XX("W17_7XX_USB_VBUSI", 2, 29, 6, 28, 1, 0) +MUX_CFG_7XX("W18_7XX_USB_DMCK_OUT",3, 3, 1, 2, 0, 0) +MUX_CFG_7XX("W19_7XX_USB_DCRST", 3, 7, 1, 6, 0, 0) + +/* MMC Pins */ +MUX_CFG_7XX("MMC_7XX_CMD", 2, 9, 0, 8, 1, 0) +MUX_CFG_7XX("MMC_7XX_CLK", 2, 13, 0, 12, 1, 0) +MUX_CFG_7XX("MMC_7XX_DAT0", 2, 17, 0, 16, 1, 0) + +/* I2C interface */ +MUX_CFG_7XX("I2C_7XX_SCL", 5, 1, 0, 0, 1, 0) +MUX_CFG_7XX("I2C_7XX_SDA", 5, 5, 0, 0, 1, 0) + +/* SPI pins */ +MUX_CFG_7XX("SPI_7XX_1", 6, 5, 4, 4, 1, 0) +MUX_CFG_7XX("SPI_7XX_2", 6, 9, 4, 8, 1, 0) +MUX_CFG_7XX("SPI_7XX_3", 6, 13, 4, 12, 1, 0) +MUX_CFG_7XX("SPI_7XX_4", 6, 17, 4, 16, 1, 0) +MUX_CFG_7XX("SPI_7XX_5", 8, 25, 0, 24, 0, 0) +MUX_CFG_7XX("SPI_7XX_6", 9, 5, 0, 4, 0, 0) + +/* UART pins */ +MUX_CFG_7XX("UART_7XX_1", 3, 21, 0, 20, 0, 0) +MUX_CFG_7XX("UART_7XX_2", 8, 1, 6, 0, 0, 0) +}; +#define OMAP7XX_PINS_SZ ARRAY_SIZE(omap7xx_pins) +#else +#define omap7xx_pins NULL +#define OMAP7XX_PINS_SZ 0 +#endif /* CONFIG_ARCH_OMAP730 || CONFIG_ARCH_OMAP850 */ + +#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) +static struct pin_config omap1xxx_pins[] = { +/* + * description mux mode mux pull pull pull pu_pd pu dbg + * reg offset mode reg bit ena reg + */ +MUX_CFG("UART1_TX", 9, 21, 1, 2, 3, 0, NA, 0, 0) +MUX_CFG("UART1_RTS", 9, 12, 1, 2, 0, 0, NA, 0, 0) + +/* UART2 (COM_UART_GATING), conflicts with USB2 */ +MUX_CFG("UART2_TX", C, 27, 1, 3, 3, 0, NA, 0, 0) +MUX_CFG("UART2_RX", C, 18, 0, 3, 1, 1, NA, 0, 0) +MUX_CFG("UART2_CTS", C, 21, 0, 3, 1, 1, NA, 0, 0) +MUX_CFG("UART2_RTS", C, 24, 1, 3, 2, 0, NA, 0, 0) + +/* UART3 (GIGA_UART_GATING) */ +MUX_CFG("UART3_TX", 6, 0, 1, 0, 30, 0, NA, 0, 0) +MUX_CFG("UART3_RX", 6, 3, 0, 0, 31, 1, NA, 0, 0) +MUX_CFG("UART3_CTS", 5, 12, 2, 0, 24, 0, NA, 0, 0) +MUX_CFG("UART3_RTS", 5, 15, 2, 0, 25, 0, NA, 0, 0) +MUX_CFG("UART3_CLKREQ", 9, 27, 0, 2, 5, 0, NA, 0, 0) +MUX_CFG("UART3_BCLK", A, 0, 0, 2, 6, 0, NA, 0, 0) +MUX_CFG("Y15_1610_UART3_RTS", A, 0, 1, 2, 6, 0, NA, 0, 0) + +/* PWT & PWL, conflicts with UART3 */ +MUX_CFG("PWT", 6, 0, 2, 0, 30, 0, NA, 0, 0) +MUX_CFG("PWL", 6, 3, 1, 0, 31, 1, NA, 0, 0) + +/* USB internal master generic */ +MUX_CFG("R18_USB_VBUS", 7, 9, 2, 1, 11, 0, NA, 0, 1) +MUX_CFG("R18_1510_USB_GPIO0", 7, 9, 0, 1, 11, 1, NA, 0, 1) +/* works around erratum: W4_USB_PUEN and W4_USB_PUDIS are switched! */ +MUX_CFG("W4_USB_PUEN", D, 3, 3, 3, 5, 1, NA, 0, 1) +MUX_CFG("W4_USB_CLKO", D, 3, 1, 3, 5, 0, NA, 0, 1) +MUX_CFG("W4_USB_HIGHZ", D, 3, 4, 3, 5, 0, 3, 0, 1) +MUX_CFG("W4_GPIO58", D, 3, 7, 3, 5, 0, 3, 0, 1) + +/* USB1 master */ +MUX_CFG("USB1_SUSP", 8, 27, 2, 1, 27, 0, NA, 0, 1) +MUX_CFG("USB1_SE0", 9, 0, 2, 1, 28, 0, NA, 0, 1) +MUX_CFG("W13_1610_USB1_SE0", 9, 0, 4, 1, 28, 0, NA, 0, 1) +MUX_CFG("USB1_TXEN", 9, 3, 2, 1, 29, 0, NA, 0, 1) +MUX_CFG("USB1_TXD", 9, 24, 1, 2, 4, 0, NA, 0, 1) +MUX_CFG("USB1_VP", A, 3, 1, 2, 7, 0, NA, 0, 1) +MUX_CFG("USB1_VM", A, 6, 1, 2, 8, 0, NA, 0, 1) +MUX_CFG("USB1_RCV", A, 9, 1, 2, 9, 0, NA, 0, 1) +MUX_CFG("USB1_SPEED", A, 12, 2, 2, 10, 0, NA, 0, 1) +MUX_CFG("R13_1610_USB1_SPEED", A, 12, 5, 2, 10, 0, NA, 0, 1) +MUX_CFG("R13_1710_USB1_SEO", A, 12, 5, 2, 10, 0, NA, 0, 1) + +/* USB2 master */ +MUX_CFG("USB2_SUSP", B, 3, 1, 2, 17, 0, NA, 0, 1) +MUX_CFG("USB2_VP", B, 6, 1, 2, 18, 0, NA, 0, 1) +MUX_CFG("USB2_TXEN", B, 9, 1, 2, 19, 0, NA, 0, 1) +MUX_CFG("USB2_VM", C, 18, 1, 3, 0, 0, NA, 0, 1) +MUX_CFG("USB2_RCV", C, 21, 1, 3, 1, 0, NA, 0, 1) +MUX_CFG("USB2_SE0", C, 24, 2, 3, 2, 0, NA, 0, 1) +MUX_CFG("USB2_TXD", C, 27, 2, 3, 3, 0, NA, 0, 1) + +/* OMAP-1510 GPIO */ +MUX_CFG("R18_1510_GPIO0", 7, 9, 0, 1, 11, 1, 0, 0, 1) +MUX_CFG("R19_1510_GPIO1", 7, 6, 0, 1, 10, 1, 0, 0, 1) +MUX_CFG("M14_1510_GPIO2", 7, 3, 0, 1, 9, 1, 0, 0, 1) + +/* OMAP1610 GPIO */ +MUX_CFG("P18_1610_GPIO3", 7, 0, 0, 1, 8, 0, NA, 0, 1) +MUX_CFG("Y15_1610_GPIO17", A, 0, 7, 2, 6, 0, NA, 0, 1) + +/* OMAP-1710 GPIO */ +MUX_CFG("R18_1710_GPIO0", 7, 9, 0, 1, 11, 1, 1, 1, 1) +MUX_CFG("V2_1710_GPIO10", F, 27, 1, 4, 3, 1, 4, 1, 1) +MUX_CFG("N21_1710_GPIO14", 6, 9, 0, 1, 1, 1, 1, 1, 1) +MUX_CFG("W15_1710_GPIO40", 9, 27, 7, 2, 5, 1, 2, 1, 1) + +/* MPUIO */ +MUX_CFG("MPUIO2", 7, 18, 0, 1, 14, 1, NA, 0, 1) +MUX_CFG("N15_1610_MPUIO2", 7, 18, 0, 1, 14, 1, 1, 0, 1) +MUX_CFG("MPUIO4", 7, 15, 0, 1, 13, 1, NA, 0, 1) +MUX_CFG("MPUIO5", 7, 12, 0, 1, 12, 1, NA, 0, 1) + +MUX_CFG("T20_1610_MPUIO5", 7, 12, 0, 1, 12, 0, 3, 0, 1) +MUX_CFG("W11_1610_MPUIO6", 10, 15, 2, 3, 8, 0, 3, 0, 1) +MUX_CFG("V10_1610_MPUIO7", A, 24, 2, 2, 14, 0, 2, 0, 1) +MUX_CFG("W11_1610_MPUIO9", 10, 15, 1, 3, 8, 0, 3, 0, 1) +MUX_CFG("V10_1610_MPUIO10", A, 24, 1, 2, 14, 0, 2, 0, 1) +MUX_CFG("W10_1610_MPUIO11", A, 18, 2, 2, 11, 0, 2, 0, 1) +MUX_CFG("E20_1610_MPUIO13", 3, 21, 1, 0, 7, 0, 0, 0, 1) +MUX_CFG("U20_1610_MPUIO14", 9, 6, 6, 0, 30, 0, 0, 0, 1) +MUX_CFG("E19_1610_MPUIO15", 3, 18, 1, 0, 6, 0, 0, 0, 1) + +/* MCBSP2 */ +MUX_CFG("MCBSP2_CLKR", C, 6, 0, 2, 27, 1, NA, 0, 1) +MUX_CFG("MCBSP2_CLKX", C, 9, 0, 2, 29, 1, NA, 0, 1) +MUX_CFG("MCBSP2_DR", C, 0, 0, 2, 26, 1, NA, 0, 1) +MUX_CFG("MCBSP2_DX", C, 15, 0, 2, 31, 1, NA, 0, 1) +MUX_CFG("MCBSP2_FSR", C, 12, 0, 2, 30, 1, NA, 0, 1) +MUX_CFG("MCBSP2_FSX", C, 3, 0, 2, 27, 1, NA, 0, 1) + +/* MCBSP3 NOTE: Mode must 1 for clock */ +MUX_CFG("MCBSP3_CLKX", 9, 3, 1, 1, 29, 0, NA, 0, 1) + +/* Misc ballouts */ +MUX_CFG("BALLOUT_V8_ARMIO3", B, 18, 0, 2, 25, 1, NA, 0, 1) +MUX_CFG("N20_HDQ", 6, 18, 1, 1, 4, 0, 1, 4, 0) + +/* OMAP-1610 MMC2 */ +MUX_CFG("W8_1610_MMC2_DAT0", B, 21, 6, 2, 23, 1, 2, 1, 1) +MUX_CFG("V8_1610_MMC2_DAT1", B, 27, 6, 2, 25, 1, 2, 1, 1) +MUX_CFG("W15_1610_MMC2_DAT2", 9, 12, 6, 2, 5, 1, 2, 1, 1) +MUX_CFG("R10_1610_MMC2_DAT3", B, 18, 6, 2, 22, 1, 2, 1, 1) +MUX_CFG("Y10_1610_MMC2_CLK", B, 3, 6, 2, 17, 0, 2, 0, 1) +MUX_CFG("Y8_1610_MMC2_CMD", B, 24, 6, 2, 24, 1, 2, 1, 1) +MUX_CFG("V9_1610_MMC2_CMDDIR", B, 12, 6, 2, 20, 0, 2, 1, 1) +MUX_CFG("V5_1610_MMC2_DATDIR0", B, 15, 6, 2, 21, 0, 2, 1, 1) +MUX_CFG("W19_1610_MMC2_DATDIR1", 8, 15, 6, 1, 23, 0, 1, 1, 1) +MUX_CFG("R18_1610_MMC2_CLKIN", 7, 9, 6, 1, 11, 0, 1, 11, 1) + +/* OMAP-1610 External Trace Interface */ +MUX_CFG("M19_1610_ETM_PSTAT0", 5, 27, 1, 0, 29, 0, 0, 0, 1) +MUX_CFG("L15_1610_ETM_PSTAT1", 5, 24, 1, 0, 28, 0, 0, 0, 1) +MUX_CFG("L18_1610_ETM_PSTAT2", 5, 21, 1, 0, 27, 0, 0, 0, 1) +MUX_CFG("L19_1610_ETM_D0", 5, 18, 1, 0, 26, 0, 0, 0, 1) +MUX_CFG("J19_1610_ETM_D6", 5, 0, 1, 0, 20, 0, 0, 0, 1) +MUX_CFG("J18_1610_ETM_D7", 5, 27, 1, 0, 19, 0, 0, 0, 1) + +/* OMAP16XX GPIO */ +MUX_CFG("P20_1610_GPIO4", 6, 27, 0, 1, 7, 0, 1, 1, 1) +MUX_CFG("V9_1610_GPIO7", B, 12, 1, 2, 20, 0, 2, 1, 1) +MUX_CFG("W8_1610_GPIO9", B, 21, 0, 2, 23, 0, 2, 1, 1) +MUX_CFG("N20_1610_GPIO11", 6, 18, 0, 1, 4, 0, 1, 1, 1) +MUX_CFG("N19_1610_GPIO13", 6, 12, 0, 1, 2, 0, 1, 1, 1) +MUX_CFG("P10_1610_GPIO22", C, 0, 7, 2, 26, 0, 2, 1, 1) +MUX_CFG("V5_1610_GPIO24", B, 15, 7, 2, 21, 0, 2, 1, 1) +MUX_CFG("AA20_1610_GPIO_41", 9, 9, 7, 1, 31, 0, 1, 1, 1) +MUX_CFG("W19_1610_GPIO48", 8, 15, 7, 1, 23, 1, 1, 0, 1) +MUX_CFG("M7_1610_GPIO62", 10, 0, 0, 4, 24, 0, 4, 0, 1) +MUX_CFG("V14_16XX_GPIO37", 9, 18, 7, 2, 2, 0, 2, 2, 0) +MUX_CFG("R9_16XX_GPIO18", C, 18, 7, 3, 0, 0, 3, 0, 0) +MUX_CFG("L14_16XX_GPIO49", 6, 3, 7, 0, 31, 0, 0, 31, 0) + +/* OMAP-1610 uWire */ +MUX_CFG("V19_1610_UWIRE_SCLK", 8, 6, 0, 1, 20, 0, 1, 1, 1) +MUX_CFG("U18_1610_UWIRE_SDI", 8, 0, 0, 1, 18, 0, 1, 1, 1) +MUX_CFG("W21_1610_UWIRE_SDO", 8, 3, 0, 1, 19, 0, 1, 1, 1) +MUX_CFG("N14_1610_UWIRE_CS0", 8, 9, 1, 1, 21, 0, 1, 1, 1) +MUX_CFG("P15_1610_UWIRE_CS3", 8, 12, 1, 1, 22, 0, 1, 1, 1) +MUX_CFG("N15_1610_UWIRE_CS1", 7, 18, 2, 1, 14, 0, NA, 0, 1) + +/* OMAP-1610 SPI */ +MUX_CFG("U19_1610_SPIF_SCK", 7, 21, 6, 1, 15, 0, 1, 1, 1) +MUX_CFG("U18_1610_SPIF_DIN", 8, 0, 6, 1, 18, 1, 1, 0, 1) +MUX_CFG("P20_1610_SPIF_DIN", 6, 27, 4, 1, 7, 1, 1, 0, 1) +MUX_CFG("W21_1610_SPIF_DOUT", 8, 3, 6, 1, 19, 0, 1, 0, 1) +MUX_CFG("R18_1610_SPIF_DOUT", 7, 9, 3, 1, 11, 0, 1, 0, 1) +MUX_CFG("N14_1610_SPIF_CS0", 8, 9, 6, 1, 21, 0, 1, 1, 1) +MUX_CFG("N15_1610_SPIF_CS1", 7, 18, 6, 1, 14, 0, 1, 1, 1) +MUX_CFG("T19_1610_SPIF_CS2", 7, 15, 4, 1, 13, 0, 1, 1, 1) +MUX_CFG("P15_1610_SPIF_CS3", 8, 12, 3, 1, 22, 0, 1, 1, 1) + +/* OMAP-1610 Flash */ +MUX_CFG("L3_1610_FLASH_CS2B_OE",10, 6, 1, NA, 0, 0, NA, 0, 1) +MUX_CFG("M8_1610_FLASH_CS2B_WE",10, 3, 1, NA, 0, 0, NA, 0, 1) + +/* First MMC interface, same on 1510, 1610 and 1710 */ +MUX_CFG("MMC_CMD", A, 27, 0, 2, 15, 1, 2, 1, 1) +MUX_CFG("MMC_DAT1", A, 24, 0, 2, 14, 1, 2, 1, 1) +MUX_CFG("MMC_DAT2", A, 18, 0, 2, 12, 1, 2, 1, 1) +MUX_CFG("MMC_DAT0", B, 0, 0, 2, 16, 1, 2, 1, 1) +MUX_CFG("MMC_CLK", A, 21, 0, NA, 0, 0, NA, 0, 1) +MUX_CFG("MMC_DAT3", 10, 15, 0, 3, 8, 1, 3, 1, 1) +MUX_CFG("M15_1710_MMC_CLKI", 6, 21, 2, 0, 0, 0, NA, 0, 1) +MUX_CFG("P19_1710_MMC_CMDDIR", 6, 24, 6, 0, 0, 0, NA, 0, 1) +MUX_CFG("P20_1710_MMC_DATDIR0", 6, 27, 5, 0, 0, 0, NA, 0, 1) + +/* OMAP-1610 USB0 alternate configuration */ +MUX_CFG("W9_USB0_TXEN", B, 9, 5, 2, 19, 0, 2, 0, 1) +MUX_CFG("AA9_USB0_VP", B, 6, 5, 2, 18, 0, 2, 0, 1) +MUX_CFG("Y5_USB0_RCV", C, 21, 5, 3, 1, 0, 1, 0, 1) +MUX_CFG("R9_USB0_VM", C, 18, 5, 3, 0, 0, 3, 0, 1) +MUX_CFG("V6_USB0_TXD", C, 27, 5, 3, 3, 0, 3, 0, 1) +MUX_CFG("W5_USB0_SE0", C, 24, 5, 3, 2, 0, 3, 0, 1) +MUX_CFG("V9_USB0_SPEED", B, 12, 5, 2, 20, 0, 2, 0, 1) +MUX_CFG("Y10_USB0_SUSP", B, 3, 5, 2, 17, 0, 2, 0, 1) + +/* USB2 interface */ +MUX_CFG("W9_USB2_TXEN", B, 9, 1, NA, 0, 0, NA, 0, 1) +MUX_CFG("AA9_USB2_VP", B, 6, 1, NA, 0, 0, NA, 0, 1) +MUX_CFG("Y5_USB2_RCV", C, 21, 1, NA, 0, 0, NA, 0, 1) +MUX_CFG("R9_USB2_VM", C, 18, 1, NA, 0, 0, NA, 0, 1) +MUX_CFG("V6_USB2_TXD", C, 27, 2, NA, 0, 0, NA, 0, 1) +MUX_CFG("W5_USB2_SE0", C, 24, 2, NA, 0, 0, NA, 0, 1) + +/* 16XX UART */ +MUX_CFG("R13_1610_UART1_TX", A, 12, 6, 2, 10, 0, 2, 10, 1) +MUX_CFG("V14_16XX_UART1_RX", 9, 18, 0, 2, 2, 0, 2, 2, 1) +MUX_CFG("R14_1610_UART1_CTS", 9, 15, 0, 2, 1, 0, 2, 1, 1) +MUX_CFG("AA15_1610_UART1_RTS", 9, 12, 1, 2, 0, 0, 2, 0, 1) +MUX_CFG("R9_16XX_UART2_RX", C, 18, 0, 3, 0, 0, 3, 0, 1) +MUX_CFG("L14_16XX_UART3_RX", 6, 3, 0, 0, 31, 0, 0, 31, 1) + +/* I2C interface */ +MUX_CFG("I2C_SCL", 7, 24, 0, NA, 0, 0, NA, 0, 0) +MUX_CFG("I2C_SDA", 7, 27, 0, NA, 0, 0, NA, 0, 0) + +/* Keypad */ +MUX_CFG("F18_1610_KBC0", 3, 15, 0, 0, 5, 1, 0, 0, 0) +MUX_CFG("D20_1610_KBC1", 3, 12, 0, 0, 4, 1, 0, 0, 0) +MUX_CFG("D19_1610_KBC2", 3, 9, 0, 0, 3, 1, 0, 0, 0) +MUX_CFG("E18_1610_KBC3", 3, 6, 0, 0, 2, 1, 0, 0, 0) +MUX_CFG("C21_1610_KBC4", 3, 3, 0, 0, 1, 1, 0, 0, 0) +MUX_CFG("G18_1610_KBR0", 4, 0, 0, 0, 10, 1, 0, 1, 0) +MUX_CFG("F19_1610_KBR1", 3, 27, 0, 0, 9, 1, 0, 1, 0) +MUX_CFG("H14_1610_KBR2", 3, 24, 0, 0, 8, 1, 0, 1, 0) +MUX_CFG("E20_1610_KBR3", 3, 21, 0, 0, 7, 1, 0, 1, 0) +MUX_CFG("E19_1610_KBR4", 3, 18, 0, 0, 6, 1, 0, 1, 0) +MUX_CFG("N19_1610_KBR5", 6, 12, 1, 1, 2, 1, 1, 1, 0) + +/* Power management */ +MUX_CFG("T20_1610_LOW_PWR", 7, 12, 1, NA, 0, 0, NA, 0, 0) + +/* MCLK Settings */ +MUX_CFG("V5_1710_MCLK_ON", B, 15, 0, NA, 0, 0, NA, 0, 0) +MUX_CFG("V5_1710_MCLK_OFF", B, 15, 6, NA, 0, 0, NA, 0, 0) +MUX_CFG("R10_1610_MCLK_ON", B, 18, 0, NA, 22, 0, NA, 1, 0) +MUX_CFG("R10_1610_MCLK_OFF", B, 18, 6, 2, 22, 1, 2, 1, 1) + +/* CompactFlash controller, conflicts with MMC1 */ +MUX_CFG("P11_1610_CF_CD2", A, 27, 3, 2, 15, 1, 2, 1, 1) +MUX_CFG("R11_1610_CF_IOIS16", B, 0, 3, 2, 16, 1, 2, 1, 1) +MUX_CFG("V10_1610_CF_IREQ", A, 24, 3, 2, 14, 0, 2, 0, 1) +MUX_CFG("W10_1610_CF_RESET", A, 18, 3, 2, 12, 1, 2, 1, 1) +MUX_CFG("W11_1610_CF_CD1", 10, 15, 3, 3, 8, 1, 3, 1, 1) + +/* parallel camera */ +MUX_CFG("J15_1610_CAM_LCLK", 4, 24, 0, 0, 18, 1, 0, 0, 0) +MUX_CFG("J18_1610_CAM_D7", 4, 27, 0, 0, 19, 1, 0, 0, 0) +MUX_CFG("J19_1610_CAM_D6", 5, 0, 0, 0, 20, 1, 0, 0, 0) +MUX_CFG("J14_1610_CAM_D5", 5, 3, 0, 0, 21, 1, 0, 0, 0) +MUX_CFG("K18_1610_CAM_D4", 5, 6, 0, 0, 22, 1, 0, 0, 0) +MUX_CFG("K19_1610_CAM_D3", 5, 9, 0, 0, 23, 1, 0, 0, 0) +MUX_CFG("K15_1610_CAM_D2", 5, 12, 0, 0, 24, 1, 0, 0, 0) +MUX_CFG("K14_1610_CAM_D1", 5, 15, 0, 0, 25, 1, 0, 0, 0) +MUX_CFG("L19_1610_CAM_D0", 5, 18, 0, 0, 26, 1, 0, 0, 0) +MUX_CFG("L18_1610_CAM_VS", 5, 21, 0, 0, 27, 1, 0, 0, 0) +MUX_CFG("L15_1610_CAM_HS", 5, 24, 0, 0, 28, 1, 0, 0, 0) +MUX_CFG("M19_1610_CAM_RSTZ", 5, 27, 0, 0, 29, 0, 0, 0, 0) +MUX_CFG("Y15_1610_CAM_OUTCLK", A, 0, 6, 2, 6, 0, 2, 0, 0) + +/* serial camera */ +MUX_CFG("H19_1610_CAM_EXCLK", 4, 21, 0, 0, 17, 0, 0, 0, 0) + /* REVISIT 5912 spec sez CCP_* can't pullup or pulldown ... ? */ +MUX_CFG("Y12_1610_CCP_CLKP", 8, 18, 6, 1, 24, 1, 1, 0, 0) +MUX_CFG("W13_1610_CCP_CLKM", 9, 0, 6, 1, 28, 1, 1, 0, 0) +MUX_CFG("W14_1610_CCP_DATAP", 9, 24, 6, 2, 4, 1, 2, 0, 0) +MUX_CFG("Y14_1610_CCP_DATAM", 9, 21, 6, 2, 3, 1, 2, 0, 0) +}; +#define OMAP1XXX_PINS_SZ ARRAY_SIZE(omap1xxx_pins) +#else +#define omap1xxx_pins NULL +#define OMAP1XXX_PINS_SZ 0 +#endif /* CONFIG_ARCH_OMAP15XX || CONFIG_ARCH_OMAP16XX */ + +static int omap1_cfg_reg(const struct pin_config *cfg) +{ + static DEFINE_SPINLOCK(mux_spin_lock); + unsigned long flags; + unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, + pull_orig = 0, pull = 0; + unsigned int mask, warn = 0; + + /* Check the mux register in question */ + if (cfg->mux_reg) { + unsigned tmp1, tmp2; + + spin_lock_irqsave(&mux_spin_lock, flags); + reg_orig = omap_readl(cfg->mux_reg); + + /* The mux registers always seem to be 3 bits long */ + mask = (0x7 << cfg->mask_offset); + tmp1 = reg_orig & mask; + reg = reg_orig & ~mask; + + tmp2 = (cfg->mask << cfg->mask_offset); + reg |= tmp2; + + if (tmp1 != tmp2) + warn = 1; + + omap_writel(reg, cfg->mux_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + /* Check for pull up or pull down selection on 1610 */ + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + spin_lock_irqsave(&mux_spin_lock, flags); + pu_pd_orig = omap_readl(cfg->pu_pd_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pu_pd_val) { + if (!(pu_pd_orig & mask)) + warn = 1; + /* Use pull up */ + pu_pd = pu_pd_orig | mask; + } else { + if (pu_pd_orig & mask) + warn = 1; + /* Use pull down */ + pu_pd = pu_pd_orig & ~mask; + } + omap_writel(pu_pd, cfg->pu_pd_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + } + + /* Check for an associated pull down register */ + if (cfg->pull_reg) { + spin_lock_irqsave(&mux_spin_lock, flags); + pull_orig = omap_readl(cfg->pull_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pull_val) { + if (pull_orig & mask) + warn = 1; + /* Low bit = pull enabled */ + pull = pull_orig & ~mask; + } else { + if (!(pull_orig & mask)) + warn = 1; + /* High bit = pull disabled */ + pull = pull_orig | mask; + } + + omap_writel(pull, cfg->pull_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + if (warn) { +#ifdef CONFIG_OMAP_MUX_WARNINGS + printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); +#endif + } + +#ifdef CONFIG_OMAP_MUX_DEBUG + if (cfg->debug || warn) { + printk("MUX: Setting register %s\n", cfg->name); + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); + + if (!cpu_is_omap15xx()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pu_pd_name, cfg->pu_pd_reg, + pu_pd_orig, pu_pd); + } + } + + if (cfg->pull_reg) + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pull_name, cfg->pull_reg, pull_orig, pull); + } +#endif + +#ifdef CONFIG_OMAP_MUX_WARNINGS + return warn ? -ETXTBSY : 0; +#else + return 0; +#endif +} + +static struct omap_mux_cfg *mux_cfg; + +int __init omap_mux_register(struct omap_mux_cfg *arch_mux_cfg) +{ + if (!arch_mux_cfg || !arch_mux_cfg->pins || arch_mux_cfg->size == 0 + || !arch_mux_cfg->cfg_reg) { + printk(KERN_ERR "Invalid pin table\n"); + return -EINVAL; + } + + mux_cfg = arch_mux_cfg; + + return 0; +} + +/* + * Sets the Omap MUX and PULL_DWN registers based on the table + */ +int omap_cfg_reg(const unsigned long index) +{ + struct pin_config *reg; + + if (!cpu_class_is_omap1()) { + printk(KERN_ERR "mux: Broken omap_cfg_reg(%lu) entry\n", + index); + WARN_ON(1); + return -EINVAL; + } + + if (mux_cfg == NULL) { + printk(KERN_ERR "Pin mux table not initialized\n"); + return -ENODEV; + } + + if (index >= mux_cfg->size) { + printk(KERN_ERR "Invalid pin mux index: %lu (%lu)\n", + index, mux_cfg->size); + dump_stack(); + return -ENODEV; + } + + reg = &mux_cfg->pins[index]; + + if (!mux_cfg->cfg_reg) + return -ENODEV; + + return mux_cfg->cfg_reg(reg); +} +EXPORT_SYMBOL(omap_cfg_reg); + +int __init omap1_mux_init(void) +{ + if (cpu_is_omap7xx()) { + arch_mux_cfg.pins = omap7xx_pins; + arch_mux_cfg.size = OMAP7XX_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + arch_mux_cfg.pins = omap1xxx_pins; + arch_mux_cfg.size = OMAP1XXX_PINS_SZ; + arch_mux_cfg.cfg_reg = omap1_cfg_reg; + } + + return omap_mux_register(&arch_mux_cfg); +} + +#else +#define omap_mux_init() do {} while(0) +#define omap_cfg_reg(x) do {} while(0) +#endif /* CONFIG_OMAP_MUX */ + diff --git a/arch/arm/mach-omap1/mux.h b/arch/arm/mach-omap1/mux.h new file mode 100644 index 000000000..3e7ed3348 --- /dev/null +++ b/arch/arm/mach-omap1/mux.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Table of the Omap register configurations for the FUNC_MUX and + * PULL_DWN combinations. + * + * Copyright (C) 2004 - 2008 Texas Instruments Inc. + * Copyright (C) 2003 - 2008 Nokia Corporation + * + * Written by Tony Lindgren + * + * NOTE: Please use the following naming style for new pin entries. + * For example, W8_1610_MMC2_DAT0, where: + * - W8 = ball + * - 1610 = 1510 or 1610, none if common for both 1510 and 1610 + * - MMC2_DAT0 = function + */ + +#ifndef __ASM_ARCH_MUX_H +#define __ASM_ARCH_MUX_H + +#include <linux/soc/ti/omap1-mux.h> + +#define PU_PD_SEL_NA 0 /* No pu_pd reg available */ +#define PULL_DWN_CTRL_NA 0 /* No pull-down control needed */ + +#ifdef CONFIG_OMAP_MUX_DEBUG +#define MUX_REG(reg, mode_offset, mode) .mux_reg_name = "FUNC_MUX_CTRL_"#reg, \ + .mux_reg = FUNC_MUX_CTRL_##reg, \ + .mask_offset = mode_offset, \ + .mask = mode, + +#define PULL_REG(reg, bit, status) .pull_name = "PULL_DWN_CTRL_"#reg, \ + .pull_reg = PULL_DWN_CTRL_##reg, \ + .pull_bit = bit, \ + .pull_val = status, + +#define PU_PD_REG(reg, status) .pu_pd_name = "PU_PD_SEL_"#reg, \ + .pu_pd_reg = PU_PD_SEL_##reg, \ + .pu_pd_val = status, + +#define MUX_REG_7XX(reg, mode_offset, mode) .mux_reg_name = "OMAP7XX_IO_CONF_"#reg, \ + .mux_reg = OMAP7XX_IO_CONF_##reg, \ + .mask_offset = mode_offset, \ + .mask = mode, + +#define PULL_REG_7XX(reg, bit, status) .pull_name = "OMAP7XX_IO_CONF_"#reg, \ + .pull_reg = OMAP7XX_IO_CONF_##reg, \ + .pull_bit = bit, \ + .pull_val = status, + +#else + +#define MUX_REG(reg, mode_offset, mode) .mux_reg = FUNC_MUX_CTRL_##reg, \ + .mask_offset = mode_offset, \ + .mask = mode, + +#define PULL_REG(reg, bit, status) .pull_reg = PULL_DWN_CTRL_##reg, \ + .pull_bit = bit, \ + .pull_val = status, + +#define PU_PD_REG(reg, status) .pu_pd_reg = PU_PD_SEL_##reg, \ + .pu_pd_val = status, + +#define MUX_REG_7XX(reg, mode_offset, mode) \ + .mux_reg = OMAP7XX_IO_CONF_##reg, \ + .mask_offset = mode_offset, \ + .mask = mode, + +#define PULL_REG_7XX(reg, bit, status) .pull_reg = OMAP7XX_IO_CONF_##reg, \ + .pull_bit = bit, \ + .pull_val = status, + +#endif /* CONFIG_OMAP_MUX_DEBUG */ + +#define MUX_CFG(desc, mux_reg, mode_offset, mode, \ + pull_reg, pull_bit, pull_status, \ + pu_pd_reg, pu_pd_status, debug_status) \ +{ \ + .name = desc, \ + .debug = debug_status, \ + MUX_REG(mux_reg, mode_offset, mode) \ + PULL_REG(pull_reg, pull_bit, pull_status) \ + PU_PD_REG(pu_pd_reg, pu_pd_status) \ +}, + + +/* + * OMAP730/850 has a slightly different config for the pin mux. + * - config regs are the OMAP7XX_IO_CONF_x regs (see omap7xx.h) regs and + * not the FUNC_MUX_CTRL_x regs from hardware.h + * - for pull-up/down, only has one enable bit which is in the same register + * as mux config + */ +#define MUX_CFG_7XX(desc, mux_reg, mode_offset, mode, \ + pull_bit, pull_status, debug_status)\ +{ \ + .name = desc, \ + .debug = debug_status, \ + MUX_REG_7XX(mux_reg, mode_offset, mode) \ + PULL_REG_7XX(mux_reg, pull_bit, pull_status) \ + PU_PD_REG(NA, 0) \ +}, + +struct pin_config { + char *name; + const unsigned int mux_reg; + unsigned char debug; + + const unsigned char mask_offset; + const unsigned char mask; + + const char *pull_name; + const unsigned int pull_reg; + const unsigned char pull_val; + const unsigned char pull_bit; + + const char *pu_pd_name; + const unsigned int pu_pd_reg; + const unsigned char pu_pd_val; + +#if defined(CONFIG_OMAP_MUX_DEBUG) || defined(CONFIG_OMAP_MUX_WARNINGS) + const char *mux_reg_name; +#endif + +}; + +struct omap_mux_cfg { + struct pin_config *pins; + unsigned long size; + int (*cfg_reg)(const struct pin_config *cfg); +}; + +#ifdef CONFIG_OMAP_MUX +/* setup pin muxing in Linux */ +extern int omap1_mux_init(void); +extern int omap_mux_register(struct omap_mux_cfg *); +#else +/* boot loader does it all (no warnings from CONFIG_OMAP_MUX_WARNINGS) */ +static inline int omap1_mux_init(void) { return 0; } +#endif + +extern int omap2_mux_init(void); + +#endif diff --git a/arch/arm/mach-omap1/ocpi.c b/arch/arm/mach-omap1/ocpi.c new file mode 100644 index 000000000..d48f72657 --- /dev/null +++ b/arch/arm/mach-omap1/ocpi.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/arch/arm/plat-omap/ocpi.c + * + * Minimal OCP bus support for omap16xx + * + * Copyright (C) 2003 - 2005 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Written by Tony Lindgren <tony@atomide.com> + * + * Modified for clock framework by Paul Mundt <paul.mundt@nokia.com>. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/soc/ti/omap1-io.h> + +#include "hardware.h" +#include "common.h" + +#define OCPI_BASE 0xfffec320 +#define OCPI_FAULT (OCPI_BASE + 0x00) +#define OCPI_CMD_FAULT (OCPI_BASE + 0x04) +#define OCPI_SINT0 (OCPI_BASE + 0x08) +#define OCPI_TABORT (OCPI_BASE + 0x0c) +#define OCPI_SINT1 (OCPI_BASE + 0x10) +#define OCPI_PROT (OCPI_BASE + 0x14) +#define OCPI_SEC (OCPI_BASE + 0x18) + +/* USB OHCI OCPI access error registers */ +#define HOSTUEADDR 0xfffba0e0 +#define HOSTUESTATUS 0xfffba0e4 + +static struct clk *ocpi_ck; + +/* + * Enables device access to OMAP buses via the OCPI bridge + */ +int ocpi_enable(void) +{ + unsigned int val; + + if (!cpu_is_omap16xx()) + return -ENODEV; + + /* Enable access for OHCI in OCPI */ + val = omap_readl(OCPI_PROT); + val &= ~0xff; + /* val &= (1 << 0); Allow access only to EMIFS */ + omap_writel(val, OCPI_PROT); + + val = omap_readl(OCPI_SEC); + val &= ~0xff; + omap_writel(val, OCPI_SEC); + + return 0; +} +EXPORT_SYMBOL(ocpi_enable); + +static int __init omap_ocpi_init(void) +{ + if (!cpu_is_omap16xx()) + return -ENODEV; + + ocpi_ck = clk_get(NULL, "l3_ocpi_ck"); + if (IS_ERR(ocpi_ck)) + return PTR_ERR(ocpi_ck); + + clk_prepare_enable(ocpi_ck); + ocpi_enable(); + pr_info("OMAP OCPI interconnect driver loaded\n"); + + return 0; +} + +static void __exit omap_ocpi_exit(void) +{ + /* REVISIT: Disable OCPI */ + + if (!cpu_is_omap16xx()) + return; + + clk_disable_unprepare(ocpi_ck); + clk_put(ocpi_ck); +} + +MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); +MODULE_DESCRIPTION("OMAP OCPI bus controller module"); +MODULE_LICENSE("GPL"); +module_init(omap_ocpi_init); +module_exit(omap_ocpi_exit); diff --git a/arch/arm/mach-omap1/omap-dma.c b/arch/arm/mach-omap1/omap-dma.c new file mode 100644 index 000000000..f7e62de42 --- /dev/null +++ b/arch/arm/mach-omap1/omap-dma.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/plat-omap/dma.c + * + * Copyright (C) 2003 - 2008 Nokia Corporation + * Author: Juha Yrjölä <juha.yrjola@nokia.com> + * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> + * Graphics DMA and LCD DMA graphics tranformations + * by Imre Deak <imre.deak@nokia.com> + * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. + * Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com> + * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Support functions for the OMAP internal DMA channels. + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Converted DMA library into DMA platform driver. + * - G, Manjunath Kondaiah <manjugk@ti.com> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#include <linux/omap-dma.h> + +#include <linux/soc/ti/omap1-io.h> +#include <linux/soc/ti/omap1-soc.h> + +#include "tc.h" + +/* + * MAX_LOGICAL_DMA_CH_COUNT: the maximum number of logical DMA + * channels that an instance of the SDMA IP block can support. Used + * to size arrays. (The actual maximum on a particular SoC may be less + * than this -- for example, OMAP1 SDMA instances only support 17 logical + * DMA channels.) + */ +#define MAX_LOGICAL_DMA_CH_COUNT 32 + +#undef DEBUG + +#define OMAP_DMA_ACTIVE 0x01 + +#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) + +static struct omap_system_dma_plat_info *p; +static struct omap_dma_dev_attr *d; +static int enable_1510_mode; +static u32 errata; + +struct dma_link_info { + int *linked_dmach_q; + int no_of_lchs_linked; + + int q_count; + int q_tail; + int q_head; + + int chain_state; + int chain_mode; + +}; + +static int dma_lch_count; +static int dma_chan_count; +static int omap_dma_reserve_channels; + +static DEFINE_SPINLOCK(dma_chan_lock); +static struct omap_dma_lch *dma_chan; + +static inline void omap_disable_channel_irq(int lch) +{ + /* disable channel interrupts */ + p->dma_write(0, CICR, lch); + /* Clear CSR */ + p->dma_read(CSR, lch); +} + +static inline void set_gdma_dev(int req, int dev) +{ + u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; + int shift = ((req - 1) % 5) * 6; + u32 l; + + l = omap_readl(reg); + l &= ~(0x3f << shift); + l |= (dev - 1) << shift; + omap_writel(l, reg); +} + +#if IS_ENABLED(CONFIG_FB_OMAP) +void omap_set_dma_priority(int lch, int dst_port, int priority) +{ + unsigned long reg; + u32 l; + + if (dma_omap1()) { + switch (dst_port) { + case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ + reg = OMAP_TC_OCPT1_PRIOR; + break; + case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ + reg = OMAP_TC_OCPT2_PRIOR; + break; + case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ + reg = OMAP_TC_EMIFF_PRIOR; + break; + case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ + reg = OMAP_TC_EMIFS_PRIOR; + break; + default: + BUG(); + return; + } + l = omap_readl(reg); + l &= ~(0xf << 8); + l |= (priority & 0xf) << 8; + omap_writel(l, reg); + } +} +EXPORT_SYMBOL(omap_set_dma_priority); +#endif + +#if IS_ENABLED(CONFIG_USB_OMAP) +#ifdef CONFIG_ARCH_OMAP15XX +/* Returns 1 if the DMA module is in OMAP1510-compatible mode, 0 otherwise */ +static int omap_dma_in_1510_mode(void) +{ + return enable_1510_mode; +} +#else +#define omap_dma_in_1510_mode() 0 +#endif + +void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, + int frame_count, int sync_mode, + int dma_trigger, int src_or_dst_synch) +{ + u32 l; + u16 ccr; + + l = p->dma_read(CSDP, lch); + l &= ~0x03; + l |= data_type; + p->dma_write(l, CSDP, lch); + + ccr = p->dma_read(CCR, lch); + ccr &= ~(1 << 5); + if (sync_mode == OMAP_DMA_SYNC_FRAME) + ccr |= 1 << 5; + p->dma_write(ccr, CCR, lch); + + ccr = p->dma_read(CCR2, lch); + ccr &= ~(1 << 2); + if (sync_mode == OMAP_DMA_SYNC_BLOCK) + ccr |= 1 << 2; + p->dma_write(ccr, CCR2, lch); + p->dma_write(elem_count, CEN, lch); + p->dma_write(frame_count, CFN, lch); +} +EXPORT_SYMBOL(omap_set_dma_transfer_params); + +void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode) +{ + if (!dma_omap15xx()) { + u32 l; + + l = p->dma_read(LCH_CTRL, lch); + l &= ~0x7; + l |= mode; + p->dma_write(l, LCH_CTRL, lch); + } +} +EXPORT_SYMBOL(omap_set_dma_channel_mode); + +/* Note that src_port is only for omap1 */ +void omap_set_dma_src_params(int lch, int src_port, int src_amode, + unsigned long src_start, + int src_ei, int src_fi) +{ + u32 l; + u16 w; + + w = p->dma_read(CSDP, lch); + w &= ~(0x1f << 2); + w |= src_port << 2; + p->dma_write(w, CSDP, lch); + + l = p->dma_read(CCR, lch); + l &= ~(0x03 << 12); + l |= src_amode << 12; + p->dma_write(l, CCR, lch); + + p->dma_write(src_start, CSSA, lch); + + p->dma_write(src_ei, CSEI, lch); + p->dma_write(src_fi, CSFI, lch); +} +EXPORT_SYMBOL(omap_set_dma_src_params); + +void omap_set_dma_src_data_pack(int lch, int enable) +{ + u32 l; + + l = p->dma_read(CSDP, lch); + l &= ~(1 << 6); + if (enable) + l |= (1 << 6); + p->dma_write(l, CSDP, lch); +} +EXPORT_SYMBOL(omap_set_dma_src_data_pack); + +void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) +{ + unsigned int burst = 0; + u32 l; + + l = p->dma_read(CSDP, lch); + l &= ~(0x03 << 7); + + switch (burst_mode) { + case OMAP_DMA_DATA_BURST_DIS: + break; + case OMAP_DMA_DATA_BURST_4: + burst = 0x2; + break; + case OMAP_DMA_DATA_BURST_8: + /* + * not supported by current hardware on OMAP1 + * w |= (0x03 << 7); + */ + fallthrough; + case OMAP_DMA_DATA_BURST_16: + /* OMAP1 don't support burst 16 */ + fallthrough; + default: + BUG(); + } + + l |= (burst << 7); + p->dma_write(l, CSDP, lch); +} +EXPORT_SYMBOL(omap_set_dma_src_burst_mode); + +/* Note that dest_port is only for OMAP1 */ +void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, + unsigned long dest_start, + int dst_ei, int dst_fi) +{ + u32 l; + + l = p->dma_read(CSDP, lch); + l &= ~(0x1f << 9); + l |= dest_port << 9; + p->dma_write(l, CSDP, lch); + + l = p->dma_read(CCR, lch); + l &= ~(0x03 << 14); + l |= dest_amode << 14; + p->dma_write(l, CCR, lch); + + p->dma_write(dest_start, CDSA, lch); + + p->dma_write(dst_ei, CDEI, lch); + p->dma_write(dst_fi, CDFI, lch); +} +EXPORT_SYMBOL(omap_set_dma_dest_params); + +void omap_set_dma_dest_data_pack(int lch, int enable) +{ + u32 l; + + l = p->dma_read(CSDP, lch); + l &= ~(1 << 13); + if (enable) + l |= 1 << 13; + p->dma_write(l, CSDP, lch); +} +EXPORT_SYMBOL(omap_set_dma_dest_data_pack); + +void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) +{ + unsigned int burst = 0; + u32 l; + + l = p->dma_read(CSDP, lch); + l &= ~(0x03 << 14); + + switch (burst_mode) { + case OMAP_DMA_DATA_BURST_DIS: + break; + case OMAP_DMA_DATA_BURST_4: + burst = 0x2; + break; + case OMAP_DMA_DATA_BURST_8: + burst = 0x3; + break; + case OMAP_DMA_DATA_BURST_16: + /* OMAP1 don't support burst 16 */ + fallthrough; + default: + printk(KERN_ERR "Invalid DMA burst mode\n"); + BUG(); + return; + } + l |= (burst << 14); + p->dma_write(l, CSDP, lch); +} +EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); + +static inline void omap_enable_channel_irq(int lch) +{ + /* Clear CSR */ + p->dma_read(CSR, lch); + + /* Enable some nice interrupts. */ + p->dma_write(dma_chan[lch].enabled_irqs, CICR, lch); +} + +void omap_disable_dma_irq(int lch, u16 bits) +{ + dma_chan[lch].enabled_irqs &= ~bits; +} +EXPORT_SYMBOL(omap_disable_dma_irq); + +static inline void enable_lnk(int lch) +{ + u32 l; + + l = p->dma_read(CLNK_CTRL, lch); + + l &= ~(1 << 14); + + /* Set the ENABLE_LNK bits */ + if (dma_chan[lch].next_lch != -1) + l = dma_chan[lch].next_lch | (1 << 15); + + p->dma_write(l, CLNK_CTRL, lch); +} + +static inline void disable_lnk(int lch) +{ + u32 l; + + l = p->dma_read(CLNK_CTRL, lch); + + /* Disable interrupts */ + omap_disable_channel_irq(lch); + + /* Set the STOP_LNK bit */ + l |= 1 << 14; + + p->dma_write(l, CLNK_CTRL, lch); + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} +#endif + +int omap_request_dma(int dev_id, const char *dev_name, + void (*callback)(int lch, u16 ch_status, void *data), + void *data, int *dma_ch_out) +{ + int ch, free_ch = -1; + unsigned long flags; + struct omap_dma_lch *chan; + + WARN(strcmp(dev_name, "DMA engine"), "Using deprecated platform DMA API - please update to DMA engine"); + + spin_lock_irqsave(&dma_chan_lock, flags); + for (ch = 0; ch < dma_chan_count; ch++) { + if (free_ch == -1 && dma_chan[ch].dev_id == -1) { + free_ch = ch; + /* Exit after first free channel found */ + break; + } + } + if (free_ch == -1) { + spin_unlock_irqrestore(&dma_chan_lock, flags); + return -EBUSY; + } + chan = dma_chan + free_ch; + chan->dev_id = dev_id; + + if (p->clear_lch_regs) + p->clear_lch_regs(free_ch); + + spin_unlock_irqrestore(&dma_chan_lock, flags); + + chan->dev_name = dev_name; + chan->callback = callback; + chan->data = data; + chan->flags = 0; + + chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; + + chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ; + + if (dma_omap16xx()) { + /* If the sync device is set, configure it dynamically. */ + if (dev_id != 0) { + set_gdma_dev(free_ch + 1, dev_id); + dev_id = free_ch + 1; + } + /* + * Disable the 1510 compatibility mode and set the sync device + * id. + */ + p->dma_write(dev_id | (1 << 10), CCR, free_ch); + } else { + p->dma_write(dev_id, CCR, free_ch); + } + + *dma_ch_out = free_ch; + + return 0; +} +EXPORT_SYMBOL(omap_request_dma); + +void omap_free_dma(int lch) +{ + unsigned long flags; + + if (dma_chan[lch].dev_id == -1) { + pr_err("omap_dma: trying to free unallocated DMA channel %d\n", + lch); + return; + } + + /* Disable all DMA interrupts for the channel. */ + omap_disable_channel_irq(lch); + + /* Make sure the DMA transfer is stopped. */ + p->dma_write(0, CCR, lch); + + spin_lock_irqsave(&dma_chan_lock, flags); + dma_chan[lch].dev_id = -1; + dma_chan[lch].next_lch = -1; + dma_chan[lch].callback = NULL; + spin_unlock_irqrestore(&dma_chan_lock, flags); +} +EXPORT_SYMBOL(omap_free_dma); + +/* + * Clears any DMA state so the DMA engine is ready to restart with new buffers + * through omap_start_dma(). Any buffers in flight are discarded. + */ +static void omap_clear_dma(int lch) +{ + unsigned long flags; + + local_irq_save(flags); + p->clear_dma(lch); + local_irq_restore(flags); +} + +#if IS_ENABLED(CONFIG_USB_OMAP) +void omap_start_dma(int lch) +{ + u32 l; + + /* + * The CPC/CDAC register needs to be initialized to zero + * before starting dma transfer. + */ + if (dma_omap15xx()) + p->dma_write(0, CPC, lch); + else + p->dma_write(0, CDAC, lch); + + if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + int next_lch, cur_lch; + char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT]; + + /* Set the link register of the first channel */ + enable_lnk(lch); + + memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); + dma_chan_link_map[lch] = 1; + + cur_lch = dma_chan[lch].next_lch; + do { + next_lch = dma_chan[cur_lch].next_lch; + + /* The loop case: we've been here already */ + if (dma_chan_link_map[cur_lch]) + break; + /* Mark the current channel */ + dma_chan_link_map[cur_lch] = 1; + + enable_lnk(cur_lch); + omap_enable_channel_irq(cur_lch); + + cur_lch = next_lch; + } while (next_lch != -1); + } else if (IS_DMA_ERRATA(DMA_ERRATA_PARALLEL_CHANNELS)) + p->dma_write(lch, CLNK_CTRL, lch); + + omap_enable_channel_irq(lch); + + l = p->dma_read(CCR, lch); + + if (IS_DMA_ERRATA(DMA_ERRATA_IFRAME_BUFFERING)) + l |= OMAP_DMA_CCR_BUFFERING_DISABLE; + l |= OMAP_DMA_CCR_EN; + + /* + * As dma_write() uses IO accessors which are weakly ordered, there + * is no guarantee that data in coherent DMA memory will be visible + * to the DMA device. Add a memory barrier here to ensure that any + * such data is visible prior to enabling DMA. + */ + mb(); + p->dma_write(l, CCR, lch); + + dma_chan[lch].flags |= OMAP_DMA_ACTIVE; +} +EXPORT_SYMBOL(omap_start_dma); + +void omap_stop_dma(int lch) +{ + u32 l; + + /* Disable all interrupts on the channel */ + omap_disable_channel_irq(lch); + + l = p->dma_read(CCR, lch); + if (IS_DMA_ERRATA(DMA_ERRATA_i541) && + (l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) { + int i = 0; + u32 sys_cf; + + /* Configure No-Standby */ + l = p->dma_read(OCP_SYSCONFIG, lch); + sys_cf = l; + l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK; + l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE); + p->dma_write(l , OCP_SYSCONFIG, 0); + + l = p->dma_read(CCR, lch); + l &= ~OMAP_DMA_CCR_EN; + p->dma_write(l, CCR, lch); + + /* Wait for sDMA FIFO drain */ + l = p->dma_read(CCR, lch); + while (i < 100 && (l & (OMAP_DMA_CCR_RD_ACTIVE | + OMAP_DMA_CCR_WR_ACTIVE))) { + udelay(5); + i++; + l = p->dma_read(CCR, lch); + } + if (i >= 100) + pr_err("DMA drain did not complete on lch %d\n", lch); + /* Restore OCP_SYSCONFIG */ + p->dma_write(sys_cf, OCP_SYSCONFIG, lch); + } else { + l &= ~OMAP_DMA_CCR_EN; + p->dma_write(l, CCR, lch); + } + + /* + * Ensure that data transferred by DMA is visible to any access + * after DMA has been disabled. This is important for coherent + * DMA regions. + */ + mb(); + + if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + int next_lch, cur_lch = lch; + char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT]; + + memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); + do { + /* The loop case: we've been here already */ + if (dma_chan_link_map[cur_lch]) + break; + /* Mark the current channel */ + dma_chan_link_map[cur_lch] = 1; + + disable_lnk(cur_lch); + + next_lch = dma_chan[cur_lch].next_lch; + cur_lch = next_lch; + } while (next_lch != -1); + } + + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} +EXPORT_SYMBOL(omap_stop_dma); + +/* + * Allows changing the DMA callback function or data. This may be needed if + * the driver shares a single DMA channel for multiple dma triggers. + */ +/* + * Returns current physical source address for the given DMA channel. + * If the channel is running the caller must disable interrupts prior calling + * this function and process the returned value before re-enabling interrupt to + * prevent races with the interrupt handler. Note that in continuous mode there + * is a chance for CSSA_L register overflow between the two reads resulting + * in incorrect return value. + */ +dma_addr_t omap_get_dma_src_pos(int lch) +{ + dma_addr_t offset = 0; + + if (dma_omap15xx()) + offset = p->dma_read(CPC, lch); + else + offset = p->dma_read(CSAC, lch); + + if (IS_DMA_ERRATA(DMA_ERRATA_3_3) && offset == 0) + offset = p->dma_read(CSAC, lch); + + if (!dma_omap15xx()) { + /* + * CDAC == 0 indicates that the DMA transfer on the channel has + * not been started (no data has been transferred so far). + * Return the programmed source start address in this case. + */ + if (likely(p->dma_read(CDAC, lch))) + offset = p->dma_read(CSAC, lch); + else + offset = p->dma_read(CSSA, lch); + } + + offset |= (p->dma_read(CSSA, lch) & 0xFFFF0000); + + return offset; +} +EXPORT_SYMBOL(omap_get_dma_src_pos); + +/* + * Returns current physical destination address for the given DMA channel. + * If the channel is running the caller must disable interrupts prior calling + * this function and process the returned value before re-enabling interrupt to + * prevent races with the interrupt handler. Note that in continuous mode there + * is a chance for CDSA_L register overflow between the two reads resulting + * in incorrect return value. + */ +dma_addr_t omap_get_dma_dst_pos(int lch) +{ + dma_addr_t offset = 0; + + if (dma_omap15xx()) + offset = p->dma_read(CPC, lch); + else + offset = p->dma_read(CDAC, lch); + + /* + * omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is + * read before the DMA controller finished disabling the channel. + */ + if (!dma_omap15xx() && offset == 0) { + offset = p->dma_read(CDAC, lch); + /* + * CDAC == 0 indicates that the DMA transfer on the channel has + * not been started (no data has been transferred so far). + * Return the programmed destination start address in this case. + */ + if (unlikely(!offset)) + offset = p->dma_read(CDSA, lch); + } + + offset |= (p->dma_read(CDSA, lch) & 0xFFFF0000); + + return offset; +} +EXPORT_SYMBOL(omap_get_dma_dst_pos); + +int omap_get_dma_active_status(int lch) +{ + return (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) != 0; +} +EXPORT_SYMBOL(omap_get_dma_active_status); +#endif + +int omap_dma_running(void) +{ + int lch; + + if (omap_lcd_dma_running()) + return 1; + + for (lch = 0; lch < dma_chan_count; lch++) + if (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) + return 1; + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static int omap1_dma_handle_ch(int ch) +{ + u32 csr; + + if (enable_1510_mode && ch >= 6) { + csr = dma_chan[ch].saved_csr; + dma_chan[ch].saved_csr = 0; + } else + csr = p->dma_read(CSR, ch); + if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { + dma_chan[ch + 6].saved_csr = csr >> 7; + csr &= 0x7f; + } + if ((csr & 0x3f) == 0) + return 0; + if (unlikely(dma_chan[ch].dev_id == -1)) { + pr_warn("Spurious interrupt from DMA channel %d (CSR %04x)\n", + ch, csr); + return 0; + } + if (unlikely(csr & OMAP1_DMA_TOUT_IRQ)) + pr_warn("DMA timeout with device %d\n", dma_chan[ch].dev_id); + if (unlikely(csr & OMAP_DMA_DROP_IRQ)) + pr_warn("DMA synchronization event drop occurred with device %d\n", + dma_chan[ch].dev_id); + if (likely(csr & OMAP_DMA_BLOCK_IRQ)) + dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; + if (likely(dma_chan[ch].callback != NULL)) + dma_chan[ch].callback(ch, csr, dma_chan[ch].data); + + return 1; +} + +static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id) +{ + int ch = ((int) dev_id) - 1; + int handled = 0; + + for (;;) { + int handled_now = 0; + + handled_now += omap1_dma_handle_ch(ch); + if (enable_1510_mode && dma_chan[ch + 6].saved_csr) + handled_now += omap1_dma_handle_ch(ch + 6); + if (!handled_now) + break; + handled += handled_now; + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +struct omap_system_dma_plat_info *omap_get_plat_info(void) +{ + return p; +} +EXPORT_SYMBOL_GPL(omap_get_plat_info); + +static int omap_system_dma_probe(struct platform_device *pdev) +{ + int ch, ret = 0; + int dma_irq; + char irq_name[4]; + + p = pdev->dev.platform_data; + if (!p) { + dev_err(&pdev->dev, + "%s: System DMA initialized without platform data\n", + __func__); + return -EINVAL; + } + + d = p->dma_attr; + errata = p->errata; + + if ((d->dev_caps & RESERVE_CHANNEL) && omap_dma_reserve_channels + && (omap_dma_reserve_channels < d->lch_count)) + d->lch_count = omap_dma_reserve_channels; + + dma_lch_count = d->lch_count; + dma_chan_count = dma_lch_count; + enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; + + dma_chan = devm_kcalloc(&pdev->dev, dma_lch_count, + sizeof(*dma_chan), GFP_KERNEL); + if (!dma_chan) + return -ENOMEM; + + for (ch = 0; ch < dma_chan_count; ch++) { + omap_clear_dma(ch); + + dma_chan[ch].dev_id = -1; + dma_chan[ch].next_lch = -1; + + if (ch >= 6 && enable_1510_mode) + continue; + + /* + * request_irq() doesn't like dev_id (ie. ch) being + * zero, so we have to kludge around this. + */ + sprintf(&irq_name[0], "%d", ch); + dma_irq = platform_get_irq_byname(pdev, irq_name); + + if (dma_irq < 0) { + ret = dma_irq; + goto exit_dma_irq_fail; + } + + /* INT_DMA_LCD is handled in lcd_dma.c */ + if (dma_irq == INT_DMA_LCD) + continue; + + ret = request_irq(dma_irq, + omap1_dma_irq_handler, 0, "DMA", + (void *) (ch + 1)); + if (ret != 0) + goto exit_dma_irq_fail; + } + + /* reserve dma channels 0 and 1 in high security devices on 34xx */ + if (d->dev_caps & HS_CHANNELS_RESERVED) { + pr_info("Reserving DMA channels 0 and 1 for HS ROM code\n"); + dma_chan[0].dev_id = 0; + dma_chan[1].dev_id = 1; + } + p->show_dma_caps(); + return 0; + +exit_dma_irq_fail: + return ret; +} + +static int omap_system_dma_remove(struct platform_device *pdev) +{ + int dma_irq, irq_rel = 0; + + for ( ; irq_rel < dma_chan_count; irq_rel++) { + dma_irq = platform_get_irq(pdev, irq_rel); + free_irq(dma_irq, (void *)(irq_rel + 1)); + } + + return 0; +} + +static struct platform_driver omap_system_dma_driver = { + .probe = omap_system_dma_probe, + .remove = omap_system_dma_remove, + .driver = { + .name = "omap_dma_system" + }, +}; + +static int __init omap_system_dma_init(void) +{ + return platform_driver_register(&omap_system_dma_driver); +} +arch_initcall(omap_system_dma_init); + +static void __exit omap_system_dma_exit(void) +{ + platform_driver_unregister(&omap_system_dma_driver); +} + +MODULE_DESCRIPTION("OMAP SYSTEM DMA DRIVER"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Inc"); + +/* + * Reserve the omap SDMA channels using cmdline bootarg + * "omap_dma_reserve_ch=". The valid range is 1 to 32 + */ +static int __init omap_dma_cmdline_reserve_ch(char *str) +{ + if (get_option(&str, &omap_dma_reserve_channels) != 1) + omap_dma_reserve_channels = 0; + return 1; +} + +__setup("omap_dma_reserve_ch=", omap_dma_cmdline_reserve_ch); + + diff --git a/arch/arm/mach-omap1/omap1510.h b/arch/arm/mach-omap1/omap1510.h new file mode 100644 index 000000000..3d235244b --- /dev/null +++ b/arch/arm/mach-omap1/omap1510.h @@ -0,0 +1,162 @@ +/* + * Hardware definitions for TI OMAP1510 processor. + * + * Cleanup for Linux-2.6 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP15XX_H +#define __ASM_ARCH_OMAP15XX_H + +/* + * ---------------------------------------------------------------------------- + * Base addresses + * ---------------------------------------------------------------------------- + */ + +/* Syntax: XX_BASE = Virtual base address, XX_START = Physical base address */ + +#define OMAP1510_DSP_BASE 0xE0000000 +#define OMAP1510_DSP_SIZE 0x28000 +#define OMAP1510_DSP_START 0xE0000000 + +#define OMAP1510_DSPREG_BASE 0xE1000000 +#define OMAP1510_DSPREG_SIZE SZ_128K +#define OMAP1510_DSPREG_START 0xE1000000 + +#define OMAP1510_DSP_MMU_BASE (0xfffed200) + +/* + * --------------------------------------------------------------------------- + * OMAP-1510 FPGA + * --------------------------------------------------------------------------- + */ +#define OMAP1510_FPGA_BASE 0xE8000000 /* VA */ +#define OMAP1510_FPGA_SIZE SZ_4K +#define OMAP1510_FPGA_START 0x08000000 /* PA */ + +/* Revision */ +#define OMAP1510_FPGA_REV_LOW IOMEM(OMAP1510_FPGA_BASE + 0x0) +#define OMAP1510_FPGA_REV_HIGH IOMEM(OMAP1510_FPGA_BASE + 0x1) +#define OMAP1510_FPGA_LCD_PANEL_CONTROL IOMEM(OMAP1510_FPGA_BASE + 0x2) +#define OMAP1510_FPGA_LED_DIGIT IOMEM(OMAP1510_FPGA_BASE + 0x3) +#define INNOVATOR_FPGA_HID_SPI IOMEM(OMAP1510_FPGA_BASE + 0x4) +#define OMAP1510_FPGA_POWER IOMEM(OMAP1510_FPGA_BASE + 0x5) + +/* Interrupt status */ +#define OMAP1510_FPGA_ISR_LO IOMEM(OMAP1510_FPGA_BASE + 0x6) +#define OMAP1510_FPGA_ISR_HI IOMEM(OMAP1510_FPGA_BASE + 0x7) + +/* Interrupt mask */ +#define OMAP1510_FPGA_IMR_LO IOMEM(OMAP1510_FPGA_BASE + 0x8) +#define OMAP1510_FPGA_IMR_HI IOMEM(OMAP1510_FPGA_BASE + 0x9) + +/* Reset registers */ +#define OMAP1510_FPGA_HOST_RESET IOMEM(OMAP1510_FPGA_BASE + 0xa) +#define OMAP1510_FPGA_RST IOMEM(OMAP1510_FPGA_BASE + 0xb) + +#define OMAP1510_FPGA_AUDIO IOMEM(OMAP1510_FPGA_BASE + 0xc) +#define OMAP1510_FPGA_DIP IOMEM(OMAP1510_FPGA_BASE + 0xe) +#define OMAP1510_FPGA_FPGA_IO IOMEM(OMAP1510_FPGA_BASE + 0xf) +#define OMAP1510_FPGA_UART1 IOMEM(OMAP1510_FPGA_BASE + 0x14) +#define OMAP1510_FPGA_UART2 IOMEM(OMAP1510_FPGA_BASE + 0x15) +#define OMAP1510_FPGA_OMAP1510_STATUS IOMEM(OMAP1510_FPGA_BASE + 0x16) +#define OMAP1510_FPGA_BOARD_REV IOMEM(OMAP1510_FPGA_BASE + 0x18) +#define INNOVATOR_FPGA_CAM_USB_CONTROL IOMEM(OMAP1510_FPGA_BASE + 0x20c) +#define OMAP1510P1_PPT_DATA IOMEM(OMAP1510_FPGA_BASE + 0x100) +#define OMAP1510P1_PPT_STATUS IOMEM(OMAP1510_FPGA_BASE + 0x101) +#define OMAP1510P1_PPT_CONTROL IOMEM(OMAP1510_FPGA_BASE + 0x102) + +#define OMAP1510_FPGA_TOUCHSCREEN IOMEM(OMAP1510_FPGA_BASE + 0x204) + +#define INNOVATOR_FPGA_INFO IOMEM(OMAP1510_FPGA_BASE + 0x205) +#define INNOVATOR_FPGA_LCD_BRIGHT_LO IOMEM(OMAP1510_FPGA_BASE + 0x206) +#define INNOVATOR_FPGA_LCD_BRIGHT_HI IOMEM(OMAP1510_FPGA_BASE + 0x207) +#define INNOVATOR_FPGA_LED_GRN_LO IOMEM(OMAP1510_FPGA_BASE + 0x208) +#define INNOVATOR_FPGA_LED_GRN_HI IOMEM(OMAP1510_FPGA_BASE + 0x209) +#define INNOVATOR_FPGA_LED_RED_LO IOMEM(OMAP1510_FPGA_BASE + 0x20a) +#define INNOVATOR_FPGA_LED_RED_HI IOMEM(OMAP1510_FPGA_BASE + 0x20b) +#define INNOVATOR_FPGA_EXP_CONTROL IOMEM(OMAP1510_FPGA_BASE + 0x20d) +#define INNOVATOR_FPGA_ISR2 IOMEM(OMAP1510_FPGA_BASE + 0x20e) +#define INNOVATOR_FPGA_IMR2 IOMEM(OMAP1510_FPGA_BASE + 0x210) + +#define OMAP1510_FPGA_ETHR_START (OMAP1510_FPGA_START + 0x300) + +/* + * Power up Giga UART driver, turn on HID clock. + * Turn off BT power, since we're not using it and it + * draws power. + */ +#define OMAP1510_FPGA_RESET_VALUE 0x42 + +#define OMAP1510_FPGA_PCR_IF_PD0 (1 << 7) +#define OMAP1510_FPGA_PCR_COM2_EN (1 << 6) +#define OMAP1510_FPGA_PCR_COM1_EN (1 << 5) +#define OMAP1510_FPGA_PCR_EXP_PD0 (1 << 4) +#define OMAP1510_FPGA_PCR_EXP_PD1 (1 << 3) +#define OMAP1510_FPGA_PCR_48MHZ_CLK (1 << 2) +#define OMAP1510_FPGA_PCR_4MHZ_CLK (1 << 1) +#define OMAP1510_FPGA_PCR_RSRVD_BIT0 (1 << 0) + +/* + * Innovator/OMAP1510 FPGA HID register bit definitions + */ +#define OMAP1510_FPGA_HID_SCLK (1<<0) /* output */ +#define OMAP1510_FPGA_HID_MOSI (1<<1) /* output */ +#define OMAP1510_FPGA_HID_nSS (1<<2) /* output 0/1 chip idle/select */ +#define OMAP1510_FPGA_HID_nHSUS (1<<3) /* output 0/1 host active/suspended */ +#define OMAP1510_FPGA_HID_MISO (1<<4) /* input */ +#define OMAP1510_FPGA_HID_ATN (1<<5) /* input 0/1 chip idle/ATN */ +#define OMAP1510_FPGA_HID_rsrvd (1<<6) +#define OMAP1510_FPGA_HID_RESETn (1<<7) /* output - 0/1 USAR reset/run */ + +/* The FPGA IRQ is cascaded through GPIO_13 */ +#define OMAP1510_INT_FPGA (IH_GPIO_BASE + 13) + +/* IRQ Numbers for interrupts muxed through the FPGA */ +#define OMAP1510_INT_FPGA_ATN (OMAP_FPGA_IRQ_BASE + 0) +#define OMAP1510_INT_FPGA_ACK (OMAP_FPGA_IRQ_BASE + 1) +#define OMAP1510_INT_FPGA2 (OMAP_FPGA_IRQ_BASE + 2) +#define OMAP1510_INT_FPGA3 (OMAP_FPGA_IRQ_BASE + 3) +#define OMAP1510_INT_FPGA4 (OMAP_FPGA_IRQ_BASE + 4) +#define OMAP1510_INT_FPGA5 (OMAP_FPGA_IRQ_BASE + 5) +#define OMAP1510_INT_FPGA6 (OMAP_FPGA_IRQ_BASE + 6) +#define OMAP1510_INT_FPGA7 (OMAP_FPGA_IRQ_BASE + 7) +#define OMAP1510_INT_FPGA8 (OMAP_FPGA_IRQ_BASE + 8) +#define OMAP1510_INT_FPGA9 (OMAP_FPGA_IRQ_BASE + 9) +#define OMAP1510_INT_FPGA10 (OMAP_FPGA_IRQ_BASE + 10) +#define OMAP1510_INT_FPGA11 (OMAP_FPGA_IRQ_BASE + 11) +#define OMAP1510_INT_FPGA12 (OMAP_FPGA_IRQ_BASE + 12) +#define OMAP1510_INT_ETHER (OMAP_FPGA_IRQ_BASE + 13) +#define OMAP1510_INT_FPGAUART1 (OMAP_FPGA_IRQ_BASE + 14) +#define OMAP1510_INT_FPGAUART2 (OMAP_FPGA_IRQ_BASE + 15) +#define OMAP1510_INT_FPGA_TS (OMAP_FPGA_IRQ_BASE + 16) +#define OMAP1510_INT_FPGA17 (OMAP_FPGA_IRQ_BASE + 17) +#define OMAP1510_INT_FPGA_CAM (OMAP_FPGA_IRQ_BASE + 18) +#define OMAP1510_INT_FPGA_RTC_A (OMAP_FPGA_IRQ_BASE + 19) +#define OMAP1510_INT_FPGA_RTC_B (OMAP_FPGA_IRQ_BASE + 20) +#define OMAP1510_INT_FPGA_CD (OMAP_FPGA_IRQ_BASE + 21) +#define OMAP1510_INT_FPGA22 (OMAP_FPGA_IRQ_BASE + 22) +#define OMAP1510_INT_FPGA23 (OMAP_FPGA_IRQ_BASE + 23) + +#endif /* __ASM_ARCH_OMAP15XX_H */ + diff --git a/arch/arm/mach-omap1/omap16xx.h b/arch/arm/mach-omap1/omap16xx.h new file mode 100644 index 000000000..cd1c72486 --- /dev/null +++ b/arch/arm/mach-omap1/omap16xx.h @@ -0,0 +1,201 @@ +/* + * Hardware definitions for TI OMAP1610/5912/1710 processors. + * + * Cleanup for Linux-2.6 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP16XX_H +#define __ASM_ARCH_OMAP16XX_H + +/* + * ---------------------------------------------------------------------------- + * Base addresses + * ---------------------------------------------------------------------------- + */ + +/* Syntax: XX_BASE = Virtual base address, XX_START = Physical base address */ + +#define OMAP16XX_DSP_BASE 0xE0000000 +#define OMAP16XX_DSP_SIZE 0x28000 +#define OMAP16XX_DSP_START 0xE0000000 + +#define OMAP16XX_DSPREG_BASE 0xE1000000 +#define OMAP16XX_DSPREG_SIZE SZ_128K +#define OMAP16XX_DSPREG_START 0xE1000000 + +#define OMAP16XX_SEC_BASE 0xFFFE4000 +#define OMAP16XX_SEC_DES (OMAP16XX_SEC_BASE + 0x0000) +#define OMAP16XX_SEC_SHA1MD5 (OMAP16XX_SEC_BASE + 0x0800) +#define OMAP16XX_SEC_RNG (OMAP16XX_SEC_BASE + 0x1000) + +/* + * --------------------------------------------------------------------------- + * Interrupts + * --------------------------------------------------------------------------- + */ +#define OMAP_IH2_0_BASE (0xfffe0000) +#define OMAP_IH2_1_BASE (0xfffe0100) +#define OMAP_IH2_2_BASE (0xfffe0200) +#define OMAP_IH2_3_BASE (0xfffe0300) + +#define OMAP_IH2_0_ITR (OMAP_IH2_0_BASE + 0x00) +#define OMAP_IH2_0_MIR (OMAP_IH2_0_BASE + 0x04) +#define OMAP_IH2_0_SIR_IRQ (OMAP_IH2_0_BASE + 0x10) +#define OMAP_IH2_0_SIR_FIQ (OMAP_IH2_0_BASE + 0x14) +#define OMAP_IH2_0_CONTROL (OMAP_IH2_0_BASE + 0x18) +#define OMAP_IH2_0_ILR0 (OMAP_IH2_0_BASE + 0x1c) +#define OMAP_IH2_0_ISR (OMAP_IH2_0_BASE + 0x9c) + +#define OMAP_IH2_1_ITR (OMAP_IH2_1_BASE + 0x00) +#define OMAP_IH2_1_MIR (OMAP_IH2_1_BASE + 0x04) +#define OMAP_IH2_1_SIR_IRQ (OMAP_IH2_1_BASE + 0x10) +#define OMAP_IH2_1_SIR_FIQ (OMAP_IH2_1_BASE + 0x14) +#define OMAP_IH2_1_CONTROL (OMAP_IH2_1_BASE + 0x18) +#define OMAP_IH2_1_ILR1 (OMAP_IH2_1_BASE + 0x1c) +#define OMAP_IH2_1_ISR (OMAP_IH2_1_BASE + 0x9c) + +#define OMAP_IH2_2_ITR (OMAP_IH2_2_BASE + 0x00) +#define OMAP_IH2_2_MIR (OMAP_IH2_2_BASE + 0x04) +#define OMAP_IH2_2_SIR_IRQ (OMAP_IH2_2_BASE + 0x10) +#define OMAP_IH2_2_SIR_FIQ (OMAP_IH2_2_BASE + 0x14) +#define OMAP_IH2_2_CONTROL (OMAP_IH2_2_BASE + 0x18) +#define OMAP_IH2_2_ILR2 (OMAP_IH2_2_BASE + 0x1c) +#define OMAP_IH2_2_ISR (OMAP_IH2_2_BASE + 0x9c) + +#define OMAP_IH2_3_ITR (OMAP_IH2_3_BASE + 0x00) +#define OMAP_IH2_3_MIR (OMAP_IH2_3_BASE + 0x04) +#define OMAP_IH2_3_SIR_IRQ (OMAP_IH2_3_BASE + 0x10) +#define OMAP_IH2_3_SIR_FIQ (OMAP_IH2_3_BASE + 0x14) +#define OMAP_IH2_3_CONTROL (OMAP_IH2_3_BASE + 0x18) +#define OMAP_IH2_3_ILR3 (OMAP_IH2_3_BASE + 0x1c) +#define OMAP_IH2_3_ISR (OMAP_IH2_3_BASE + 0x9c) + +/* + * ---------------------------------------------------------------------------- + * Clocks + * ---------------------------------------------------------------------------- + */ +#define OMAP16XX_ARM_IDLECT3 (CLKGEN_REG_BASE + 0x24) + +/* + * ---------------------------------------------------------------------------- + * Pin configuration registers + * ---------------------------------------------------------------------------- + */ +#define OMAP16XX_CONF_VOLTAGE_VDDSHV6 (1 << 8) +#define OMAP16XX_CONF_VOLTAGE_VDDSHV7 (1 << 9) +#define OMAP16XX_CONF_VOLTAGE_VDDSHV8 (1 << 10) +#define OMAP16XX_CONF_VOLTAGE_VDDSHV9 (1 << 11) +#define OMAP16XX_SUBLVDS_CONF_VALID (1 << 13) + +/* + * ---------------------------------------------------------------------------- + * System control registers + * ---------------------------------------------------------------------------- + */ +#define OMAP1610_RESET_CONTROL 0xfffe1140 + +/* + * --------------------------------------------------------------------------- + * TIPB bus interface + * --------------------------------------------------------------------------- + */ +#define TIPB_SWITCH_BASE (0xfffbc800) +#define OMAP16XX_MMCSD2_SSW_MPU_CONF (TIPB_SWITCH_BASE + 0x160) + +/* UART3 Registers Mapping through MPU bus */ +#define UART3_RHR (OMAP1_UART3_BASE + 0) +#define UART3_THR (OMAP1_UART3_BASE + 0) +#define UART3_DLL (OMAP1_UART3_BASE + 0) +#define UART3_IER (OMAP1_UART3_BASE + 4) +#define UART3_DLH (OMAP1_UART3_BASE + 4) +#define UART3_IIR (OMAP1_UART3_BASE + 8) +#define UART3_FCR (OMAP1_UART3_BASE + 8) +#define UART3_EFR (OMAP1_UART3_BASE + 8) +#define UART3_LCR (OMAP1_UART3_BASE + 0x0C) +#define UART3_MCR (OMAP1_UART3_BASE + 0x10) +#define UART3_XON1_ADDR1 (OMAP1_UART3_BASE + 0x10) +#define UART3_XON2_ADDR2 (OMAP1_UART3_BASE + 0x14) +#define UART3_LSR (OMAP1_UART3_BASE + 0x14) +#define UART3_TCR (OMAP1_UART3_BASE + 0x18) +#define UART3_MSR (OMAP1_UART3_BASE + 0x18) +#define UART3_XOFF1 (OMAP1_UART3_BASE + 0x18) +#define UART3_XOFF2 (OMAP1_UART3_BASE + 0x1C) +#define UART3_SPR (OMAP1_UART3_BASE + 0x1C) +#define UART3_TLR (OMAP1_UART3_BASE + 0x1C) +#define UART3_MDR1 (OMAP1_UART3_BASE + 0x20) +#define UART3_MDR2 (OMAP1_UART3_BASE + 0x24) +#define UART3_SFLSR (OMAP1_UART3_BASE + 0x28) +#define UART3_TXFLL (OMAP1_UART3_BASE + 0x28) +#define UART3_RESUME (OMAP1_UART3_BASE + 0x2C) +#define UART3_TXFLH (OMAP1_UART3_BASE + 0x2C) +#define UART3_SFREGL (OMAP1_UART3_BASE + 0x30) +#define UART3_RXFLL (OMAP1_UART3_BASE + 0x30) +#define UART3_SFREGH (OMAP1_UART3_BASE + 0x34) +#define UART3_RXFLH (OMAP1_UART3_BASE + 0x34) +#define UART3_BLR (OMAP1_UART3_BASE + 0x38) +#define UART3_ACREG (OMAP1_UART3_BASE + 0x3C) +#define UART3_DIV16 (OMAP1_UART3_BASE + 0x3C) +#define UART3_SCR (OMAP1_UART3_BASE + 0x40) +#define UART3_SSR (OMAP1_UART3_BASE + 0x44) +#define UART3_EBLR (OMAP1_UART3_BASE + 0x48) +#define UART3_OSC_12M_SEL (OMAP1_UART3_BASE + 0x4C) +#define UART3_MVR (OMAP1_UART3_BASE + 0x50) + +/* + * --------------------------------------------------------------------------- + * Watchdog timer + * --------------------------------------------------------------------------- + */ + +/* 32-bit Watchdog timer in OMAP 16XX */ +#define OMAP_16XX_WATCHDOG_BASE (0xfffeb000) +#define OMAP_16XX_WIDR (OMAP_16XX_WATCHDOG_BASE + 0x00) +#define OMAP_16XX_WD_SYSCONFIG (OMAP_16XX_WATCHDOG_BASE + 0x10) +#define OMAP_16XX_WD_SYSSTATUS (OMAP_16XX_WATCHDOG_BASE + 0x14) +#define OMAP_16XX_WCLR (OMAP_16XX_WATCHDOG_BASE + 0x24) +#define OMAP_16XX_WCRR (OMAP_16XX_WATCHDOG_BASE + 0x28) +#define OMAP_16XX_WLDR (OMAP_16XX_WATCHDOG_BASE + 0x2c) +#define OMAP_16XX_WTGR (OMAP_16XX_WATCHDOG_BASE + 0x30) +#define OMAP_16XX_WWPS (OMAP_16XX_WATCHDOG_BASE + 0x34) +#define OMAP_16XX_WSPR (OMAP_16XX_WATCHDOG_BASE + 0x48) + +#define WCLR_PRE_SHIFT 5 +#define WCLR_PTV_SHIFT 2 + +#define WWPS_W_PEND_WSPR (1 << 4) +#define WWPS_W_PEND_WTGR (1 << 3) +#define WWPS_W_PEND_WLDR (1 << 2) +#define WWPS_W_PEND_WCRR (1 << 1) +#define WWPS_W_PEND_WCLR (1 << 0) + +#define WSPR_ENABLE_0 (0x0000bbbb) +#define WSPR_ENABLE_1 (0x00004444) +#define WSPR_DISABLE_0 (0x0000aaaa) +#define WSPR_DISABLE_1 (0x00005555) + +#define OMAP16XX_DSP_MMU_BASE (0xfffed200) +#define OMAP16XX_MAILBOX_BASE (0xfffcf000) + +#endif /* __ASM_ARCH_OMAP16XX_H */ + diff --git a/arch/arm/mach-omap1/omap7xx.h b/arch/arm/mach-omap1/omap7xx.h new file mode 100644 index 000000000..63da994bc --- /dev/null +++ b/arch/arm/mach-omap1/omap7xx.h @@ -0,0 +1,106 @@ +/* + * Hardware definitions for TI OMAP7XX processor. + * + * Cleanup for Linux-2.6 by Dirk Behme <dirk.behme@de.bosch.com> + * Adapted for omap850 by Zebediah C. McClure <zmc@lurian.net> + * Adapted for omap7xx by Alistair Buxton <a.j.buxton@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP7XX_H +#define __ASM_ARCH_OMAP7XX_H + +/* + * ---------------------------------------------------------------------------- + * Base addresses + * ---------------------------------------------------------------------------- + */ + +/* Syntax: XX_BASE = Virtual base address, XX_START = Physical base address */ + +#define OMAP7XX_DSP_BASE 0xE0000000 +#define OMAP7XX_DSP_SIZE 0x50000 +#define OMAP7XX_DSP_START 0xE0000000 + +#define OMAP7XX_DSPREG_BASE 0xE1000000 +#define OMAP7XX_DSPREG_SIZE SZ_128K +#define OMAP7XX_DSPREG_START 0xE1000000 + +#define OMAP7XX_SPI1_BASE 0xfffc0800 +#define OMAP7XX_SPI2_BASE 0xfffc1000 + +/* + * ---------------------------------------------------------------------------- + * OMAP7XX specific configuration registers + * ---------------------------------------------------------------------------- + */ +#define OMAP7XX_CONFIG_BASE 0xfffe1000 +#define OMAP7XX_IO_CONF_0 0xfffe1070 +#define OMAP7XX_IO_CONF_1 0xfffe1074 +#define OMAP7XX_IO_CONF_2 0xfffe1078 +#define OMAP7XX_IO_CONF_3 0xfffe107c +#define OMAP7XX_IO_CONF_4 0xfffe1080 +#define OMAP7XX_IO_CONF_5 0xfffe1084 +#define OMAP7XX_IO_CONF_6 0xfffe1088 +#define OMAP7XX_IO_CONF_7 0xfffe108c +#define OMAP7XX_IO_CONF_8 0xfffe1090 +#define OMAP7XX_IO_CONF_9 0xfffe1094 +#define OMAP7XX_IO_CONF_10 0xfffe1098 +#define OMAP7XX_IO_CONF_11 0xfffe109c +#define OMAP7XX_IO_CONF_12 0xfffe10a0 +#define OMAP7XX_IO_CONF_13 0xfffe10a4 + +#define OMAP7XX_MODE_1 0xfffe1010 +#define OMAP7XX_MODE_2 0xfffe1014 + +/* CSMI specials: in terms of base + offset */ +#define OMAP7XX_MODE2_OFFSET 0x14 + +/* + * ---------------------------------------------------------------------------- + * OMAP7XX traffic controller configuration registers + * ---------------------------------------------------------------------------- + */ +#define OMAP7XX_FLASH_CFG_0 0xfffecc10 +#define OMAP7XX_FLASH_ACFG_0 0xfffecc50 +#define OMAP7XX_FLASH_CFG_1 0xfffecc14 +#define OMAP7XX_FLASH_ACFG_1 0xfffecc54 + +/* + * ---------------------------------------------------------------------------- + * OMAP7XX DSP control registers + * ---------------------------------------------------------------------------- + */ +#define OMAP7XX_ICR_BASE 0xfffbb800 +#define OMAP7XX_DSP_M_CTL 0xfffbb804 +#define OMAP7XX_DSP_MMU_BASE 0xfffed200 + +/* + * ---------------------------------------------------------------------------- + * OMAP7XX PCC_UPLD configuration registers + * ---------------------------------------------------------------------------- + */ +#define OMAP7XX_PCC_UPLD_CTRL_BASE (0xfffe0900) +#define OMAP7XX_PCC_UPLD_CTRL (OMAP7XX_PCC_UPLD_CTRL_BASE + 0x00) + +#endif /* __ASM_ARCH_OMAP7XX_H */ + diff --git a/arch/arm/mach-omap1/opp.h b/arch/arm/mach-omap1/opp.h new file mode 100644 index 000000000..5b8b9c8ed --- /dev/null +++ b/arch/arm/mach-omap1/opp.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/mach-omap1/opp.h + * + * Copyright (C) 2004 - 2005 Nokia corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_OPP_H +#define __ARCH_ARM_MACH_OMAP1_OPP_H + +#include <linux/types.h> + +struct mpu_rate { + unsigned long rate; + unsigned long xtal; + unsigned long pll_rate; + __u16 ckctl_val; + __u16 dpllctl_val; + u32 flags; +}; + +extern struct mpu_rate omap1_rate_table[]; + +#endif diff --git a/arch/arm/mach-omap1/opp_data.c b/arch/arm/mach-omap1/opp_data.c new file mode 100644 index 000000000..a27ca7dc0 --- /dev/null +++ b/arch/arm/mach-omap1/opp_data.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/opp_data.c + * + * Copyright (C) 2004 - 2005 Nokia corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc + */ + +#include "clock.h" +#include "opp.h" + +/*------------------------------------------------------------------------- + * Omap1 MPU rate table + *-------------------------------------------------------------------------*/ +struct mpu_rate omap1_rate_table[] = { + /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL + * NOTE: Comment order here is different from bits in CKCTL value: + * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv + */ + { 216000000, 12000000, 216000000, 0x050d, 0x2910, /* 1/1/2/2/2/8 */ + CK_1710 }, + { 195000000, 13000000, 195000000, 0x050e, 0x2790, /* 1/1/2/2/4/8 */ + CK_7XX }, + { 192000000, 19200000, 192000000, 0x050f, 0x2510, /* 1/1/2/2/8/8 */ + CK_16XX }, + { 192000000, 12000000, 192000000, 0x050f, 0x2810, /* 1/1/2/2/8/8 */ + CK_16XX }, + { 96000000, 12000000, 192000000, 0x055f, 0x2810, /* 2/2/2/2/8/8 */ + CK_16XX }, + { 48000000, 12000000, 192000000, 0x0baf, 0x2810, /* 4/4/4/8/8/8 */ + CK_16XX }, + { 24000000, 12000000, 192000000, 0x0fff, 0x2810, /* 8/8/8/8/8/8 */ + CK_16XX }, + { 182000000, 13000000, 182000000, 0x050e, 0x2710, /* 1/1/2/2/4/8 */ + CK_7XX }, + { 168000000, 12000000, 168000000, 0x010f, 0x2710, /* 1/1/1/2/8/8 */ + CK_16XX|CK_7XX }, + { 150000000, 12000000, 150000000, 0x010a, 0x2cb0, /* 1/1/1/2/4/4 */ + CK_1510 }, + { 120000000, 12000000, 120000000, 0x010a, 0x2510, /* 1/1/1/2/4/4 */ + CK_16XX|CK_1510|CK_310|CK_7XX }, + { 96000000, 12000000, 96000000, 0x0005, 0x2410, /* 1/1/1/1/2/2 */ + CK_16XX|CK_1510|CK_310|CK_7XX }, + { 60000000, 12000000, 60000000, 0x0005, 0x2290, /* 1/1/1/1/2/2 */ + CK_16XX|CK_1510|CK_310|CK_7XX }, + { 30000000, 12000000, 60000000, 0x0555, 0x2290, /* 2/2/2/2/2/2 */ + CK_16XX|CK_1510|CK_310|CK_7XX }, + { 0, 0, 0, 0, 0 }, +}; + diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c new file mode 100644 index 000000000..fce7d2b57 --- /dev/null +++ b/arch/arm/mach-omap1/pm.c @@ -0,0 +1,698 @@ +/* + * linux/arch/arm/mach-omap1/pm.c + * + * OMAP Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Modified for the OMAP1510 by David Singleton: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/suspend.h> +#include <linux/sched.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/atomic.h> +#include <linux/cpu.h> + +#include <asm/fncpy.h> +#include <asm/system_misc.h> +#include <asm/irq.h> +#include <asm/mach/time.h> +#include <asm/mach/irq.h> + +#include <linux/soc/ti/omap1-io.h> +#include "tc.h" +#include <linux/omap-dma.h> +#include <clocksource/timer-ti-dm.h> + +#include "hardware.h" +#include "mux.h" +#include "irqs.h" +#include "iomap.h" +#include "clock.h" +#include "pm.h" +#include "soc.h" +#include "sram.h" + +static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; +static unsigned short dsp_sleep_save[DSP_SLEEP_SAVE_SIZE]; +static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; +static unsigned int mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_SIZE]; +static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; +static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; + +static unsigned short enable_dyn_sleep; + +static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%hu\n", enable_dyn_sleep); +} + +static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr, + const char * buf, size_t n) +{ + unsigned short value; + if (sscanf(buf, "%hu", &value) != 1 || + (value != 0 && value != 1) || + (value != 0 && !IS_ENABLED(CONFIG_OMAP_32K_TIMER))) { + pr_err("idle_sleep_store: Invalid value\n"); + return -EINVAL; + } + enable_dyn_sleep = value; + return n; +} + +static struct kobj_attribute sleep_while_idle_attr = + __ATTR(sleep_while_idle, 0644, idle_show, idle_store); + + +static void (*omap_sram_suspend)(unsigned long r0, unsigned long r1) = NULL; + +/* + * Let's power down on idle, but only if we are really + * idle, because once we start down the path of + * going idle we continue to do idle even if we get + * a clock tick interrupt . . + */ +void omap1_pm_idle(void) +{ + extern __u32 arm_idlect1_mask; + __u32 use_idlect1 = arm_idlect1_mask; + + local_fiq_disable(); + +#if defined(CONFIG_OMAP_MPU_TIMER) && !defined(CONFIG_OMAP_DM_TIMER) + use_idlect1 = use_idlect1 & ~(1 << 9); +#endif + +#ifdef CONFIG_OMAP_DM_TIMER + use_idlect1 = omap_dm_timer_modify_idlect_mask(use_idlect1); +#endif + + if (omap_dma_running()) + use_idlect1 &= ~(1 << 6); + + /* + * We should be able to remove the do_sleep variable and multiple + * tests above as soon as drivers, timer and DMA code have been fixed. + * Even the sleep block count should become obsolete. + */ + if ((use_idlect1 != ~0) || !enable_dyn_sleep) { + + __u32 saved_idlect1 = omap_readl(ARM_IDLECT1); + if (cpu_is_omap15xx()) + use_idlect1 &= OMAP1510_BIG_SLEEP_REQUEST; + else + use_idlect1 &= OMAP1610_IDLECT1_SLEEP_VAL; + omap_writel(use_idlect1, ARM_IDLECT1); + __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); + omap_writel(saved_idlect1, ARM_IDLECT1); + + local_fiq_enable(); + return; + } + omap_sram_suspend(omap_readl(ARM_IDLECT1), + omap_readl(ARM_IDLECT2)); + + local_fiq_enable(); +} + +/* + * Configuration of the wakeup event is board specific. For the + * moment we put it into this helper function. Later it may move + * to board specific files. + */ +static void omap_pm_wakeup_setup(void) +{ + u32 level1_wake = 0; + u32 level2_wake = OMAP_IRQ_BIT(INT_UART2); + + /* + * Turn off all interrupts except GPIO bank 1, L1-2nd level cascade, + * and the L2 wakeup interrupts: keypad and UART2. Note that the + * drivers must still separately call omap_set_gpio_wakeup() to + * wake up to a GPIO interrupt. + */ + if (cpu_is_omap7xx()) + level1_wake = OMAP_IRQ_BIT(INT_7XX_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_7XX_IH2_IRQ); + else if (cpu_is_omap15xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1510_IH2_IRQ); + else if (cpu_is_omap16xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1610_IH2_IRQ); + + omap_writel(~level1_wake, OMAP_IH1_MIR); + + if (cpu_is_omap7xx()) { + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + omap_writel(~(OMAP_IRQ_BIT(INT_7XX_WAKE_UP_REQ) | + OMAP_IRQ_BIT(INT_7XX_MPUIO_KEYPAD)), + OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + + /* INT_1610_WAKE_UP_REQ is needed for GPIO wakeup... */ + omap_writel(~OMAP_IRQ_BIT(INT_1610_WAKE_UP_REQ), + OMAP_IH2_1_MIR); + omap_writel(~0x0, OMAP_IH2_2_MIR); + omap_writel(~0x0, OMAP_IH2_3_MIR); + } + + /* New IRQ agreement, recalculate in cascade order */ + omap_writel(1, OMAP_IH2_CONTROL); + omap_writel(1, OMAP_IH1_CONTROL); +} + +#define EN_DSPCK 13 /* ARM_CKCTL */ +#define EN_APICK 6 /* ARM_IDLECT2 */ +#define DSP_EN 1 /* ARM_RSTCT1 */ + +void omap1_pm_suspend(void) +{ + unsigned long arg0 = 0, arg1 = 0; + + printk(KERN_INFO "PM: OMAP%x is trying to enter deep sleep...\n", + omap_rev()); + + omap_serial_wake_trigger(1); + + if (!cpu_is_omap15xx()) + omap_writew(0xffff, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Step 1: turn off interrupts (FIXME: NOTE: already disabled) + */ + + local_irq_disable(); + local_fiq_disable(); + + /* + * Step 2: save registers + * + * The omap is a strange/beautiful device. The caches, memory + * and register state are preserved across power saves. + * We have to save and restore very little register state to + * idle the omap. + * + * Save interrupt, MPUI, ARM and UPLD control registers. + */ + + if (cpu_is_omap7xx()) { + MPUI7XX_SAVE(OMAP_IH1_MIR); + MPUI7XX_SAVE(OMAP_IH2_0_MIR); + MPUI7XX_SAVE(OMAP_IH2_1_MIR); + MPUI7XX_SAVE(MPUI_CTRL); + MPUI7XX_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI7XX_SAVE(MPUI_DSP_API_CONFIG); + MPUI7XX_SAVE(EMIFS_CONFIG); + MPUI7XX_SAVE(EMIFF_SDRAM_CONFIG); + + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(OMAP_IH1_MIR); + MPUI1510_SAVE(OMAP_IH2_MIR); + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(OMAP_IH1_MIR); + MPUI1610_SAVE(OMAP_IH2_0_MIR); + MPUI1610_SAVE(OMAP_IH2_1_MIR); + MPUI1610_SAVE(OMAP_IH2_2_MIR); + MPUI1610_SAVE(OMAP_IH2_3_MIR); + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + } + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_STATUS_REQ); + + /* (Step 3 removed - we now allow deep sleep by default) */ + + /* + * Step 4: OMAP DSP Shutdown + */ + + /* stop DSP */ + omap_writew(omap_readw(ARM_RSTCT1) & ~(1 << DSP_EN), ARM_RSTCT1); + + /* shut down dsp_ck */ + if (!cpu_is_omap7xx()) + omap_writew(omap_readw(ARM_CKCTL) & ~(1 << EN_DSPCK), ARM_CKCTL); + + /* temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* save DSP registers */ + DSP_SAVE(DSP_IDLECT2); + + /* Stop all DSP domain clocks */ + __raw_writew(0, DSP_IDLECT2); + + /* + * Step 5: Wakeup Event Setup + */ + + omap_pm_wakeup_setup(); + + /* + * Step 6: ARM and Traffic controller shutdown + */ + + /* disable ARM watchdog */ + omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); + omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); + + /* + * Step 6b: ARM and Traffic controller shutdown + * + * Step 6 continues here. Prepare jump to power management + * assembly code in internal SRAM. + * + * Since the omap_cpu_suspend routine has been copied to + * SRAM, we'll do an indirect procedure call to it and pass the + * contents of arm_idlect1 and arm_idlect2 so it can restore + * them when it wakes up and it will return. + */ + + arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; + arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; + + /* + * Step 6c: ARM and Traffic controller shutdown + * + * Jump to assembly code. The processor will stay there + * until wake up. + */ + omap_sram_suspend(arg0, arg1); + + /* + * If we are here, processor is woken up! + */ + + /* + * Restore DSP clocks + */ + + /* again temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* Restore DSP domain clocks */ + DSP_RESTORE(DSP_IDLECT2); + + /* + * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did + */ + + if (!(cpu_is_omap15xx())) + ARM_RESTORE(ARM_IDLECT3); + ARM_RESTORE(ARM_CKCTL); + ARM_RESTORE(ARM_EWUPCT); + ARM_RESTORE(ARM_RSTCT1); + ARM_RESTORE(ARM_RSTCT2); + ARM_RESTORE(ARM_SYSST); + ULPD_RESTORE(ULPD_CLOCK_CTRL); + ULPD_RESTORE(ULPD_STATUS_REQ); + + if (cpu_is_omap7xx()) { + MPUI7XX_RESTORE(EMIFS_CONFIG); + MPUI7XX_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI7XX_RESTORE(OMAP_IH1_MIR); + MPUI7XX_RESTORE(OMAP_IH2_0_MIR); + MPUI7XX_RESTORE(OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + MPUI1510_RESTORE(MPUI_CTRL); + MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1510_RESTORE(EMIFS_CONFIG); + MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI1510_RESTORE(OMAP_IH1_MIR); + MPUI1510_RESTORE(OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + MPUI1610_RESTORE(MPUI_CTRL); + MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1610_RESTORE(EMIFS_CONFIG); + MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); + + MPUI1610_RESTORE(OMAP_IH1_MIR); + MPUI1610_RESTORE(OMAP_IH2_0_MIR); + MPUI1610_RESTORE(OMAP_IH2_1_MIR); + MPUI1610_RESTORE(OMAP_IH2_2_MIR); + MPUI1610_RESTORE(OMAP_IH2_3_MIR); + } + + if (!cpu_is_omap15xx()) + omap_writew(0, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Re-enable interrupts + */ + + local_irq_enable(); + local_fiq_enable(); + + omap_serial_wake_trigger(0); + + printk(KERN_INFO "PM: OMAP%x is re-starting from deep sleep...\n", + omap_rev()); +} + +#ifdef CONFIG_DEBUG_FS +/* + * Read system PM registers for debugging + */ +static int omap_pm_debug_show(struct seq_file *m, void *v) +{ + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + + ULPD_SAVE(ULPD_IT_STATUS); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_SOFT_REQ); + ULPD_SAVE(ULPD_STATUS_REQ); + ULPD_SAVE(ULPD_DPLL_CTRL); + ULPD_SAVE(ULPD_POWER_CTRL); + + if (cpu_is_omap7xx()) { + MPUI7XX_SAVE(MPUI_CTRL); + MPUI7XX_SAVE(MPUI_DSP_STATUS); + MPUI7XX_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI7XX_SAVE(MPUI_DSP_API_CONFIG); + MPUI7XX_SAVE(EMIFF_SDRAM_CONFIG); + MPUI7XX_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_STATUS); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_STATUS); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + } + + seq_printf(m, + "ARM_CKCTL_REG: 0x%-8x \n" + "ARM_IDLECT1_REG: 0x%-8x \n" + "ARM_IDLECT2_REG: 0x%-8x \n" + "ARM_IDLECT3_REG: 0x%-8x \n" + "ARM_EWUPCT_REG: 0x%-8x \n" + "ARM_RSTCT1_REG: 0x%-8x \n" + "ARM_RSTCT2_REG: 0x%-8x \n" + "ARM_SYSST_REG: 0x%-8x \n" + "ULPD_IT_STATUS_REG: 0x%-4x \n" + "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" + "ULPD_SOFT_REQ_REG: 0x%-4x \n" + "ULPD_DPLL_CTRL_REG: 0x%-4x \n" + "ULPD_STATUS_REQ_REG: 0x%-4x \n" + "ULPD_POWER_CTRL_REG: 0x%-4x \n", + ARM_SHOW(ARM_CKCTL), + ARM_SHOW(ARM_IDLECT1), + ARM_SHOW(ARM_IDLECT2), + ARM_SHOW(ARM_IDLECT3), + ARM_SHOW(ARM_EWUPCT), + ARM_SHOW(ARM_RSTCT1), + ARM_SHOW(ARM_RSTCT2), + ARM_SHOW(ARM_SYSST), + ULPD_SHOW(ULPD_IT_STATUS), + ULPD_SHOW(ULPD_CLOCK_CTRL), + ULPD_SHOW(ULPD_SOFT_REQ), + ULPD_SHOW(ULPD_DPLL_CTRL), + ULPD_SHOW(ULPD_STATUS_REQ), + ULPD_SHOW(ULPD_POWER_CTRL)); + + if (cpu_is_omap7xx()) { + seq_printf(m, + "MPUI7XX_CTRL_REG 0x%-8x \n" + "MPUI7XX_DSP_STATUS_REG: 0x%-8x \n" + "MPUI7XX_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI7XX_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI7XX_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI7XX_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI7XX_SHOW(MPUI_CTRL), + MPUI7XX_SHOW(MPUI_DSP_STATUS), + MPUI7XX_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI7XX_SHOW(MPUI_DSP_API_CONFIG), + MPUI7XX_SHOW(EMIFF_SDRAM_CONFIG), + MPUI7XX_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap15xx()) { + seq_printf(m, + "MPUI1510_CTRL_REG 0x%-8x \n" + "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1510_SHOW(MPUI_CTRL), + MPUI1510_SHOW(MPUI_DSP_STATUS), + MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1510_SHOW(MPUI_DSP_API_CONFIG), + MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1510_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap16xx()) { + seq_printf(m, + "MPUI1610_CTRL_REG 0x%-8x \n" + "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1610_SHOW(MPUI_CTRL), + MPUI1610_SHOW(MPUI_DSP_STATUS), + MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1610_SHOW(MPUI_DSP_API_CONFIG), + MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1610_SHOW(EMIFS_CONFIG)); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(omap_pm_debug); + +static void omap_pm_init_debugfs(void) +{ + struct dentry *d; + + d = debugfs_create_dir("pm_debug", NULL); + debugfs_create_file("omap_pm", S_IWUSR | S_IRUGO, d, NULL, + &omap_pm_debug_fops); +} + +#endif /* CONFIG_DEBUG_FS */ + +/* + * omap_pm_prepare - Do preliminary suspend work. + * + */ +static int omap_pm_prepare(void) +{ + /* We cannot sleep in idle until we have resumed */ + cpu_idle_poll_ctrl(true); + return 0; +} + + +/* + * omap_pm_enter - Actually enter a sleep state. + * @state: State we're entering. + * + */ + +static int omap_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_MEM: + omap1_pm_suspend(); + break; + default: + return -EINVAL; + } + + return 0; +} + + +/** + * omap_pm_finish - Finish up suspend sequence. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ + +static void omap_pm_finish(void) +{ + cpu_idle_poll_ctrl(false); +} + + +static irqreturn_t omap_wakeup_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + + + +static const struct platform_suspend_ops omap_pm_ops = { + .prepare = omap_pm_prepare, + .enter = omap_pm_enter, + .finish = omap_pm_finish, + .valid = suspend_valid_only_mem, +}; + +static int __init omap_pm_init(void) +{ + int error = 0; + int irq; + + if (!cpu_class_is_omap1()) + return -ENODEV; + + pr_info("Power Management for TI OMAP.\n"); + + if (!IS_ENABLED(CONFIG_OMAP_32K_TIMER)) + pr_info("OMAP1 PM: sleep states in idle disabled due to no 32KiHz timer\n"); + + if (!IS_ENABLED(CONFIG_OMAP_DM_TIMER)) + pr_info("OMAP1 PM: sleep states in idle disabled due to no DMTIMER support\n"); + + if (IS_ENABLED(CONFIG_OMAP_32K_TIMER) && + IS_ENABLED(CONFIG_OMAP_DM_TIMER)) { + /* OMAP16xx only */ + pr_info("OMAP1 PM: sleep states in idle enabled\n"); + enable_dyn_sleep = 1; + } + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + if (cpu_is_omap7xx()) { + omap_sram_suspend = omap_sram_push(omap7xx_cpu_suspend, + omap7xx_cpu_suspend_sz); + } else if (cpu_is_omap15xx()) { + omap_sram_suspend = omap_sram_push(omap1510_cpu_suspend, + omap1510_cpu_suspend_sz); + } else if (cpu_is_omap16xx()) { + omap_sram_suspend = omap_sram_push(omap1610_cpu_suspend, + omap1610_cpu_suspend_sz); + } + + if (omap_sram_suspend == NULL) { + printk(KERN_ERR "PM not initialized: Missing SRAM support\n"); + return -ENODEV; + } + + arm_pm_idle = omap1_pm_idle; + + if (cpu_is_omap7xx()) + irq = INT_7XX_WAKE_UP_REQ; + else if (cpu_is_omap16xx()) + irq = INT_1610_WAKE_UP_REQ; + else + irq = -1; + + if (irq >= 0) { + if (request_irq(irq, omap_wakeup_interrupt, 0, "peripheral wakeup", NULL)) + pr_err("Failed to request irq %d (peripheral wakeup)\n", irq); + } + + /* Program new power ramp-up time + * (0 for most boards since we don't lower voltage when in deep sleep) + */ + omap_writew(ULPD_SETUP_ANALOG_CELL_3_VAL, ULPD_SETUP_ANALOG_CELL_3); + + /* Setup ULPD POWER_CTRL_REG - enter deep sleep whenever possible */ + omap_writew(ULPD_POWER_CTRL_REG_VAL, ULPD_POWER_CTRL); + + /* Configure IDLECT3 */ + if (cpu_is_omap7xx()) + omap_writel(OMAP7XX_IDLECT3_VAL, OMAP7XX_IDLECT3); + else if (cpu_is_omap16xx()) + omap_writel(OMAP1610_IDLECT3_VAL, OMAP1610_IDLECT3); + + suspend_set_ops(&omap_pm_ops); + +#ifdef CONFIG_DEBUG_FS + omap_pm_init_debugfs(); +#endif + + error = sysfs_create_file(power_kobj, &sleep_while_idle_attr.attr); + if (error) + printk(KERN_ERR "sysfs_create_file failed: %d\n", error); + + if (cpu_is_omap16xx()) { + /* configure LOW_PWR pin */ + omap_cfg_reg(T20_1610_LOW_PWR); + } + + return error; +} +__initcall(omap_pm_init); diff --git a/arch/arm/mach-omap1/pm.h b/arch/arm/mach-omap1/pm.h new file mode 100644 index 000000000..0d1f09282 --- /dev/null +++ b/arch/arm/mach-omap1/pm.h @@ -0,0 +1,276 @@ +/* + * arch/arm/mach-omap1/pm.h + * + * Header file for OMAP1 Power Management Routines + * + * Author: MontaVista Software, Inc. + * support@mvista.com + * + * Copyright 2002 MontaVista Software Inc. + * + * Cleanup 2004 for Linux 2.6 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ARCH_ARM_MACH_OMAP1_PM_H +#define __ARCH_ARM_MACH_OMAP1_PM_H + +#include <linux/soc/ti/omap1-io.h> + +/* + * ---------------------------------------------------------------------------- + * Register and offset definitions to be used in PM assembler code + * ---------------------------------------------------------------------------- + */ +#define CLKGEN_REG_ASM_BASE OMAP1_IO_ADDRESS(0xfffece00) +#define ARM_IDLECT1_ASM_OFFSET 0x04 +#define ARM_IDLECT2_ASM_OFFSET 0x08 + +#define TCMIF_ASM_BASE OMAP1_IO_ADDRESS(0xfffecc00) +#define EMIFS_CONFIG_ASM_OFFSET 0x0c +#define EMIFF_SDRAM_CONFIG_ASM_OFFSET 0x20 + +/* + * ---------------------------------------------------------------------------- + * Power management bitmasks + * ---------------------------------------------------------------------------- + */ +#define IDLE_WAIT_CYCLES 0x00000fff +#define PERIPHERAL_ENABLE 0x2 + +#define SELF_REFRESH_MODE 0x0c000001 +#define IDLE_EMIFS_REQUEST 0xc +#define MODEM_32K_EN 0x1 +#define PER_EN 0x1 + +#define CPU_SUSPEND_SIZE 200 +#define ULPD_LOW_PWR_EN 0x0001 +#define ULPD_DEEP_SLEEP_TRANSITION_EN 0x0010 +#define ULPD_SETUP_ANALOG_CELL_3_VAL 0 +#define ULPD_POWER_CTRL_REG_VAL 0x0219 + +#define DSP_IDLE_DELAY 10 +#define DSP_IDLE 0x0040 +#define DSP_RST 0x0004 +#define DSP_ENABLE 0x0002 +#define SUFFICIENT_DSP_RESET_TIME 1000 +#define DEFAULT_MPUI_CONFIG 0x05cf +#define ENABLE_XORCLK 0x2 +#define DSP_CLOCK_ENABLE 0x2000 +#define DSP_IDLE_MODE 0x2 +#define TC_IDLE_REQUEST (0x0000000c) + +#define IRQ_LEVEL2 (1<<0) +#define IRQ_KEYBOARD (1<<1) +#define IRQ_UART2 (1<<15) + +#define PDE_BIT 0x08 +#define PWD_EN_BIT 0x04 +#define EN_PERCK_BIT 0x04 + +#define OMAP1510_DEEP_SLEEP_REQUEST 0x0ec7 +#define OMAP1510_BIG_SLEEP_REQUEST 0x0cc5 +#define OMAP1510_IDLE_LOOP_REQUEST 0x0c00 +#define OMAP1510_IDLE_CLOCK_DOMAINS 0x2 + +/* Both big sleep and deep sleep use same values. Difference is in ULPD. */ +#define OMAP1610_IDLECT1_SLEEP_VAL 0x13c7 +#define OMAP1610_IDLECT2_SLEEP_VAL 0x09c7 +#define OMAP1610_IDLECT3_VAL 0x3f +#define OMAP1610_IDLECT3_SLEEP_ORMASK 0x2c +#define OMAP1610_IDLECT3 0xfffece24 +#define OMAP1610_IDLE_LOOP_REQUEST 0x0400 + +#define OMAP7XX_IDLECT1_SLEEP_VAL 0x16c7 +#define OMAP7XX_IDLECT2_SLEEP_VAL 0x09c7 +#define OMAP7XX_IDLECT3_VAL 0x3f +#define OMAP7XX_IDLECT3 0xfffece24 +#define OMAP7XX_IDLE_LOOP_REQUEST 0x0C00 + +#ifndef __ASSEMBLER__ + +#include <linux/clk.h> + +extern struct kset power_subsys; + +extern void prevent_idle_sleep(void); +extern void allow_idle_sleep(void); + +extern void omap1_pm_idle(void); +extern void omap1_pm_suspend(void); + +extern void omap7xx_cpu_suspend(unsigned long, unsigned long); +extern void omap1510_cpu_suspend(unsigned long, unsigned long); +extern void omap1610_cpu_suspend(unsigned long, unsigned long); +extern void omap7xx_idle_loop_suspend(void); +extern void omap1510_idle_loop_suspend(void); +extern void omap1610_idle_loop_suspend(void); + +extern unsigned int omap7xx_cpu_suspend_sz; +extern unsigned int omap1510_cpu_suspend_sz; +extern unsigned int omap1610_cpu_suspend_sz; +extern unsigned int omap7xx_idle_loop_suspend_sz; +extern unsigned int omap1510_idle_loop_suspend_sz; +extern unsigned int omap1610_idle_loop_suspend_sz; + +#ifdef CONFIG_OMAP_SERIAL_WAKE +extern void omap_serial_wake_trigger(int enable); +#else +#define omap_serial_wakeup_init() {} +#define omap_serial_wake_trigger(x) {} +#endif /* CONFIG_OMAP_SERIAL_WAKE */ + +#define ARM_SAVE(x) arm_sleep_save[ARM_SLEEP_SAVE_##x] = omap_readl(x) +#define ARM_RESTORE(x) omap_writel((arm_sleep_save[ARM_SLEEP_SAVE_##x]), (x)) +#define ARM_SHOW(x) arm_sleep_save[ARM_SLEEP_SAVE_##x] + +#define DSP_SAVE(x) dsp_sleep_save[DSP_SLEEP_SAVE_##x] = __raw_readw(x) +#define DSP_RESTORE(x) __raw_writew((dsp_sleep_save[DSP_SLEEP_SAVE_##x]), (x)) +#define DSP_SHOW(x) dsp_sleep_save[DSP_SLEEP_SAVE_##x] + +#define ULPD_SAVE(x) ulpd_sleep_save[ULPD_SLEEP_SAVE_##x] = omap_readw(x) +#define ULPD_RESTORE(x) omap_writew((ulpd_sleep_save[ULPD_SLEEP_SAVE_##x]), (x)) +#define ULPD_SHOW(x) ulpd_sleep_save[ULPD_SLEEP_SAVE_##x] + +#define MPUI7XX_SAVE(x) mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_##x] = omap_readl(x) +#define MPUI7XX_RESTORE(x) omap_writel((mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_##x]), (x)) +#define MPUI7XX_SHOW(x) mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_##x] + +#define MPUI1510_SAVE(x) mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_##x] = omap_readl(x) +#define MPUI1510_RESTORE(x) omap_writel((mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_##x]), (x)) +#define MPUI1510_SHOW(x) mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_##x] + +#define MPUI1610_SAVE(x) mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_##x] = omap_readl(x) +#define MPUI1610_RESTORE(x) omap_writel((mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_##x]), (x)) +#define MPUI1610_SHOW(x) mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_##x] + +/* + * List of global OMAP registers to preserve. + * More ones like CP and general purpose register values are preserved + * with the stack pointer in sleep.S. + */ + +enum arm_save_state { + ARM_SLEEP_SAVE_START = 0, + /* + * MPU control registers 32 bits + */ + ARM_SLEEP_SAVE_ARM_CKCTL, + ARM_SLEEP_SAVE_ARM_IDLECT1, + ARM_SLEEP_SAVE_ARM_IDLECT2, + ARM_SLEEP_SAVE_ARM_IDLECT3, + ARM_SLEEP_SAVE_ARM_EWUPCT, + ARM_SLEEP_SAVE_ARM_RSTCT1, + ARM_SLEEP_SAVE_ARM_RSTCT2, + ARM_SLEEP_SAVE_ARM_SYSST, + ARM_SLEEP_SAVE_SIZE +}; + +enum dsp_save_state { + DSP_SLEEP_SAVE_START = 0, + /* + * DSP registers 16 bits + */ + DSP_SLEEP_SAVE_DSP_IDLECT2, + DSP_SLEEP_SAVE_SIZE +}; + +enum ulpd_save_state { + ULPD_SLEEP_SAVE_START = 0, + /* + * ULPD registers 16 bits + */ + ULPD_SLEEP_SAVE_ULPD_IT_STATUS, + ULPD_SLEEP_SAVE_ULPD_CLOCK_CTRL, + ULPD_SLEEP_SAVE_ULPD_SOFT_REQ, + ULPD_SLEEP_SAVE_ULPD_STATUS_REQ, + ULPD_SLEEP_SAVE_ULPD_DPLL_CTRL, + ULPD_SLEEP_SAVE_ULPD_POWER_CTRL, + ULPD_SLEEP_SAVE_SIZE +}; + +enum mpui1510_save_state { + MPUI1510_SLEEP_SAVE_START = 0, + /* + * MPUI registers 32 bits + */ + MPUI1510_SLEEP_SAVE_MPUI_CTRL, + MPUI1510_SLEEP_SAVE_MPUI_DSP_BOOT_CONFIG, + MPUI1510_SLEEP_SAVE_MPUI_DSP_API_CONFIG, + MPUI1510_SLEEP_SAVE_MPUI_DSP_STATUS, + MPUI1510_SLEEP_SAVE_EMIFF_SDRAM_CONFIG, + MPUI1510_SLEEP_SAVE_EMIFS_CONFIG, + MPUI1510_SLEEP_SAVE_OMAP_IH1_MIR, + MPUI1510_SLEEP_SAVE_OMAP_IH2_MIR, +#if defined(CONFIG_ARCH_OMAP15XX) + MPUI1510_SLEEP_SAVE_SIZE +#else + MPUI1510_SLEEP_SAVE_SIZE = 0 +#endif +}; + +enum mpui7xx_save_state { + MPUI7XX_SLEEP_SAVE_START = 0, + /* + * MPUI registers 32 bits + */ + MPUI7XX_SLEEP_SAVE_MPUI_CTRL, + MPUI7XX_SLEEP_SAVE_MPUI_DSP_BOOT_CONFIG, + MPUI7XX_SLEEP_SAVE_MPUI_DSP_API_CONFIG, + MPUI7XX_SLEEP_SAVE_MPUI_DSP_STATUS, + MPUI7XX_SLEEP_SAVE_EMIFF_SDRAM_CONFIG, + MPUI7XX_SLEEP_SAVE_EMIFS_CONFIG, + MPUI7XX_SLEEP_SAVE_OMAP_IH1_MIR, + MPUI7XX_SLEEP_SAVE_OMAP_IH2_0_MIR, + MPUI7XX_SLEEP_SAVE_OMAP_IH2_1_MIR, +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + MPUI7XX_SLEEP_SAVE_SIZE +#else + MPUI7XX_SLEEP_SAVE_SIZE = 0 +#endif +}; + +enum mpui1610_save_state { + MPUI1610_SLEEP_SAVE_START = 0, + /* + * MPUI registers 32 bits + */ + MPUI1610_SLEEP_SAVE_MPUI_CTRL, + MPUI1610_SLEEP_SAVE_MPUI_DSP_BOOT_CONFIG, + MPUI1610_SLEEP_SAVE_MPUI_DSP_API_CONFIG, + MPUI1610_SLEEP_SAVE_MPUI_DSP_STATUS, + MPUI1610_SLEEP_SAVE_EMIFF_SDRAM_CONFIG, + MPUI1610_SLEEP_SAVE_EMIFS_CONFIG, + MPUI1610_SLEEP_SAVE_OMAP_IH1_MIR, + MPUI1610_SLEEP_SAVE_OMAP_IH2_0_MIR, + MPUI1610_SLEEP_SAVE_OMAP_IH2_1_MIR, + MPUI1610_SLEEP_SAVE_OMAP_IH2_2_MIR, + MPUI1610_SLEEP_SAVE_OMAP_IH2_3_MIR, +#if defined(CONFIG_ARCH_OMAP16XX) + MPUI1610_SLEEP_SAVE_SIZE +#else + MPUI1610_SLEEP_SAVE_SIZE = 0 +#endif +}; + +#endif /* ASSEMBLER */ +#endif /* __ASM_ARCH_OMAP_PM_H */ diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c new file mode 100644 index 000000000..c04619ac0 --- /dev/null +++ b/arch/arm/mach-omap1/pm_bus.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Runtime PM support code for OMAP1 + * + * Author: Kevin Hilman, Deep Root Systems, LLC + * + * Copyright (C) 2010 Texas Instruments, Inc. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/pm_clock.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include "soc.h" + +static struct dev_pm_domain default_pm_domain = { + .ops = { + USE_PM_CLK_RUNTIME_OPS + USE_PLATFORM_PM_SLEEP_OPS + }, +}; + +static struct pm_clk_notifier_block platform_bus_notifier = { + .pm_domain = &default_pm_domain, + .con_ids = { "ick", "fck", NULL, }, +}; + +static int __init omap1_pm_runtime_init(void) +{ + if (!cpu_class_is_omap1()) + return -ENODEV; + + pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier); + + return 0; +} +core_initcall(omap1_pm_runtime_init); diff --git a/arch/arm/mach-omap1/reset.c b/arch/arm/mach-omap1/reset.c new file mode 100644 index 000000000..2eee6a696 --- /dev/null +++ b/arch/arm/mach-omap1/reset.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP1 reset support + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/reboot.h> + +#include "hardware.h" +#include "iomap.h" +#include "common.h" + +/* ARM_SYSST bit shifts related to SoC reset sources */ +#define ARM_SYSST_POR_SHIFT 5 +#define ARM_SYSST_EXT_RST_SHIFT 4 +#define ARM_SYSST_ARM_WDRST_SHIFT 2 +#define ARM_SYSST_GLOB_SWRST_SHIFT 1 + +/* Standardized reset source bits (across all OMAP SoCs) */ +#define OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT 0 +#define OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT 1 +#define OMAP_MPU_WD_RST_SRC_ID_SHIFT 3 +#define OMAP_EXTWARM_RST_SRC_ID_SHIFT 5 + + +void omap1_restart(enum reboot_mode mode, const char *cmd) +{ + /* + * Workaround for 5912/1611b bug mentioned in sprz209d.pdf p. 28 + * "Global Software Reset Affects Traffic Controller Frequency". + */ + if (cpu_is_omap5912()) { + omap_writew(omap_readw(DPLL_CTL) & ~(1 << 4), DPLL_CTL); + omap_writew(0x8, ARM_RSTCT1); + } + + omap_writew(1, ARM_RSTCT1); +} + +/** + * omap1_get_reset_sources - return the source of the SoC's last reset + * + * Returns bits that represent the last reset source for the SoC. The + * format is standardized across OMAPs for use by the OMAP watchdog. + */ +u32 omap1_get_reset_sources(void) +{ + u32 ret = 0; + u16 rs; + + rs = __raw_readw(OMAP1_IO_ADDRESS(ARM_SYSST)); + + if (rs & (1 << ARM_SYSST_POR_SHIFT)) + ret |= 1 << OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT; + if (rs & (1 << ARM_SYSST_EXT_RST_SHIFT)) + ret |= 1 << OMAP_EXTWARM_RST_SRC_ID_SHIFT; + if (rs & (1 << ARM_SYSST_ARM_WDRST_SHIFT)) + ret |= 1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT; + if (rs & (1 << ARM_SYSST_GLOB_SWRST_SHIFT)) + ret |= 1 << OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT; + + return ret; +} diff --git a/arch/arm/mach-omap1/serial.c b/arch/arm/mach-omap1/serial.c new file mode 100644 index 000000000..88928fc33 --- /dev/null +++ b/arch/arm/mach-omap1/serial.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap1/serial.c + * + * OMAP1 serial support. + */ +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/mach-types.h> + +#include "serial.h" +#include "mux.h" +#include "pm.h" +#include "soc.h" + +static struct clk * uart1_ck; +static struct clk * uart2_ck; +static struct clk * uart3_ck; + +static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, + int offset) +{ + offset <<= up->regshift; + return (unsigned int)__raw_readb(up->membase + offset); +} + +static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset, + int value) +{ + offset <<= p->regshift; + __raw_writeb(value, p->membase + offset); +} + +/* + * Internal UARTs need to be initialized for the 8250 autoconfig to work + * properly. Note that the TX watermark initialization may not be needed + * once the 8250.c watermark handling code is merged. + */ +static void __init omap_serial_reset(struct plat_serial8250_port *p) +{ + omap_serial_outp(p, UART_OMAP_MDR1, + UART_OMAP_MDR1_DISABLE); /* disable UART */ + omap_serial_outp(p, UART_OMAP_SCR, 0x08); /* TX watermark */ + omap_serial_outp(p, UART_OMAP_MDR1, + UART_OMAP_MDR1_16X_MODE); /* enable UART */ + + if (!cpu_is_omap15xx()) { + omap_serial_outp(p, UART_OMAP_SYSC, 0x01); + while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01)); + } +} + +static struct plat_serial8250_port serial_platform_data[] = { + { + .mapbase = OMAP1_UART1_BASE, + .irq = INT_UART1, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .mapbase = OMAP1_UART2_BASE, + .irq = INT_UART2, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .mapbase = OMAP1_UART3_BASE, + .irq = INT_UART3, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +/* + * Note that on Innovator-1510 UART2 pins conflict with USB2. + * By default UART2 does not work on Innovator-1510 if you have + * USB OHCI enabled. To use UART2, you must disable USB2 first. + */ +void __init omap_serial_init(void) +{ + int i; + + if (cpu_is_omap7xx()) { + serial_platform_data[0].regshift = 0; + serial_platform_data[1].regshift = 0; + serial_platform_data[0].irq = INT_7XX_UART_MODEM_1; + serial_platform_data[1].irq = INT_7XX_UART_MODEM_IRDA_2; + } + + if (cpu_is_omap15xx()) { + serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16; + } + + for (i = 0; i < ARRAY_SIZE(serial_platform_data) - 1; i++) { + + /* Don't look at UARTs higher than 2 for omap7xx */ + if (cpu_is_omap7xx() && i > 1) { + serial_platform_data[i].membase = NULL; + serial_platform_data[i].mapbase = 0; + continue; + } + + /* Static mapping, never released */ + serial_platform_data[i].membase = + ioremap(serial_platform_data[i].mapbase, SZ_2K); + if (!serial_platform_data[i].membase) { + printk(KERN_ERR "Could not ioremap uart%i\n", i); + continue; + } + switch (i) { + case 0: + uart1_ck = clk_get(NULL, "uart1_ck"); + if (IS_ERR(uart1_ck)) + printk("Could not get uart1_ck\n"); + else { + clk_prepare_enable(uart1_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart1_ck, 12000000); + } + break; + case 1: + uart2_ck = clk_get(NULL, "uart2_ck"); + if (IS_ERR(uart2_ck)) + printk("Could not get uart2_ck\n"); + else { + clk_prepare_enable(uart2_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart2_ck, 12000000); + else + clk_set_rate(uart2_ck, 48000000); + } + break; + case 2: + uart3_ck = clk_get(NULL, "uart3_ck"); + if (IS_ERR(uart3_ck)) + printk("Could not get uart3_ck\n"); + else { + clk_prepare_enable(uart3_ck); + if (cpu_is_omap15xx()) + clk_set_rate(uart3_ck, 12000000); + } + break; + } + omap_serial_reset(&serial_platform_data[i]); + } +} + +#ifdef CONFIG_OMAP_SERIAL_WAKE + +static irqreturn_t omap_serial_wake_interrupt(int irq, void *dev_id) +{ + /* Need to do something with serial port right after wake-up? */ + return IRQ_HANDLED; +} + +/* + * Reroutes serial RX lines to GPIO lines for the duration of + * sleep to allow waking up the device from serial port even + * in deep sleep. + */ +void omap_serial_wake_trigger(int enable) +{ + if (!cpu_is_omap16xx()) + return; + + if (uart1_ck != NULL) { + if (enable) + omap_cfg_reg(V14_16XX_GPIO37); + else + omap_cfg_reg(V14_16XX_UART1_RX); + } + if (uart2_ck != NULL) { + if (enable) + omap_cfg_reg(R9_16XX_GPIO18); + else + omap_cfg_reg(R9_16XX_UART2_RX); + } + if (uart3_ck != NULL) { + if (enable) + omap_cfg_reg(L14_16XX_GPIO49); + else + omap_cfg_reg(L14_16XX_UART3_RX); + } +} + +static void __init omap_serial_set_port_wakeup(int gpio_nr) +{ + int ret; + + ret = gpio_request(gpio_nr, "UART wake"); + if (ret < 0) { + printk(KERN_ERR "Could not request UART wake GPIO: %i\n", + gpio_nr); + return; + } + gpio_direction_input(gpio_nr); + ret = request_irq(gpio_to_irq(gpio_nr), &omap_serial_wake_interrupt, + IRQF_TRIGGER_RISING, "serial wakeup", NULL); + if (ret) { + gpio_free(gpio_nr); + printk(KERN_ERR "No interrupt for UART wake GPIO: %i\n", + gpio_nr); + return; + } + enable_irq_wake(gpio_to_irq(gpio_nr)); +} + +int __init omap_serial_wakeup_init(void) +{ + if (!cpu_is_omap16xx()) + return 0; + + if (uart1_ck != NULL) + omap_serial_set_port_wakeup(37); + if (uart2_ck != NULL) + omap_serial_set_port_wakeup(18); + if (uart3_ck != NULL) + omap_serial_set_port_wakeup(49); + + return 0; +} + +#endif /* CONFIG_OMAP_SERIAL_WAKE */ + +static int __init omap_init(void) +{ + if (!cpu_class_is_omap1()) + return -ENODEV; + + return platform_device_register(&serial_device); +} +arch_initcall(omap_init); diff --git a/arch/arm/mach-omap1/serial.h b/arch/arm/mach-omap1/serial.h new file mode 100644 index 000000000..4700e384c --- /dev/null +++ b/arch/arm/mach-omap1/serial.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support- Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ASM_ARCH_SERIAL_H +#define __ASM_ARCH_SERIAL_H + +#include <linux/init.h> + +/* + * Memory entry used for the DEBUG_LL UART configuration, relative to + * start of RAM. See also uncompress.h and debug-macro.S. + * + * Note that using a memory location for storing the UART configuration + * has at least two limitations: + * + * 1. Kernel uncompress code cannot overlap OMAP_UART_INFO as the + * uncompress code could then partially overwrite itself + * 2. We assume printascii is called at least once before paging_init, + * and addruart has a chance to read OMAP_UART_INFO + */ +#define OMAP_UART_INFO_OFS 0x3ffc + +#define OMAP_PORT_SHIFT 2 +#define OMAP7XX_PORT_SHIFT 0 + +#define OMAP1510_BASE_BAUD (12000000/16) +#define OMAP16XX_BASE_BAUD (48000000/16) + +/* + * DEBUG_LL port encoding stored into the UART1 scratchpad register by + * decomp_setup in uncompress.h + */ +#define OMAP1UART1 11 +#define OMAP1UART2 12 +#define OMAP1UART3 13 + +#ifndef __ASSEMBLER__ +extern void omap_serial_init(void); +#endif + +#endif diff --git a/arch/arm/mach-omap1/sleep.S b/arch/arm/mach-omap1/sleep.S new file mode 100644 index 000000000..f111b7951 --- /dev/null +++ b/arch/arm/mach-omap1/sleep.S @@ -0,0 +1,370 @@ +/* + * linux/arch/arm/mach-omap1/sleep.S + * + * Low-level OMAP7XX/1510/1610 sleep/wakeUp support + * + * Initial SA1110 code: + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * Adapted for PXA by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/linkage.h> + +#include <asm/assembler.h> + +#include "hardware.h" + +#include "iomap.h" +#include "pm.h" + + .text + + +/* + * Forces OMAP into deep sleep state + * + * omapXXXX_cpu_suspend() + * + * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed + * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 + * in register r1. + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + * + * Note: Because of errata work arounds we have processor specific functions + * here. They are mostly the same, but slightly different. + * + */ + +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + .align 3 +ENTRY(omap7xx_cpu_suspend) + + @ save registers on stack + stmfd sp!, {r0 - r12, lr} + + @ Drain write cache + mov r4, #0 + mcr p15, 0, r0, c7, c10, 4 + nop + + @ load base address of Traffic Controller + mov r6, #TCMIF_ASM_BASE & 0xff000000 + orr r6, r6, #TCMIF_ASM_BASE & 0x00ff0000 + orr r6, r6, #TCMIF_ASM_BASE & 0x0000ff00 + + @ prepare to put SDRAM into self-refresh manually + ldr r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + orr r9, r7, #SELF_REFRESH_MODE & 0xff000000 + orr r9, r9, #SELF_REFRESH_MODE & 0x000000ff + str r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put EMIFS to Sleep + ldr r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + orr r9, r8, #IDLE_EMIFS_REQUEST & 0xff + str r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + @ do not disable PERCK (0x04) + mov r5, #OMAP7XX_IDLECT2_SLEEP_VAL & 0xff + orr r5, r5, #OMAP7XX_IDLECT2_SLEEP_VAL & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + mov r3, #OMAP7XX_IDLECT1_SLEEP_VAL & 0xff + orr r3, r3, #OMAP7XX_IDLECT1_SLEEP_VAL & 0xff00 + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ disable instruction cache + mrc p15, 0, r9, c1, c0, 0 + bic r2, r9, #0x1000 + mcr p15, 0, r2, c1, c0, 0 + nop + +/* + * Let's wait for the next wake up event to wake us up. r0 can't be + * used here because r0 holds ARM_IDLECT1 + */ + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt +/* + * omap7xx_cpu_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack. + */ + @ re-enable Icache + mcr p15, 0, r9, c1, c0, 0 + + @ reset the ARM_IDLECT1 and ARM_IDLECT2. + strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ Restore EMIFF controls + str r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + str r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ restore regs and return + ldmfd sp!, {r0 - r12, pc} + +ENTRY(omap7xx_cpu_suspend_sz) + .word . - omap7xx_cpu_suspend +#endif /* CONFIG_ARCH_OMAP730 || CONFIG_ARCH_OMAP850 */ + +#ifdef CONFIG_ARCH_OMAP15XX + .align 3 +ENTRY(omap1510_cpu_suspend) + + @ save registers on stack + stmfd sp!, {r0 - r12, lr} + + @ load base address of Traffic Controller + mov r4, #TCMIF_ASM_BASE & 0xff000000 + orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 + orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 + + @ work around errata of OMAP1510 PDE bit for TC shut down + @ clear PDE bit + ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + bic r5, r5, #PDE_BIT & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ set PWD_EN bit + and r5, r5, #PWD_EN_BIT & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put SDRAM into self-refresh manually + ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 + orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff + str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put EMIFS to Sleep + ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff + orr r5, r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff + orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + mov r5, #IDLE_WAIT_CYCLES & 0xff + orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 +l_1510_2: + subs r5, r5, #1 + bne l_1510_2 +/* + * Let's wait for the next wake up event to wake us up. r0 can't be + * used here because r0 holds ARM_IDLECT1 + */ + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt +/* + * omap1510_cpu_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. + */ + strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ restore regs and return + ldmfd sp!, {r0 - r12, pc} + +ENTRY(omap1510_cpu_suspend_sz) + .word . - omap1510_cpu_suspend +#endif /* CONFIG_ARCH_OMAP15XX */ + +#if defined(CONFIG_ARCH_OMAP16XX) + .align 3 +ENTRY(omap1610_cpu_suspend) + + @ save registers on stack + stmfd sp!, {r0 - r12, lr} + + @ Drain write cache + mov r4, #0 + mcr p15, 0, r0, c7, c10, 4 + nop + + @ Load base address of Traffic Controller + mov r6, #TCMIF_ASM_BASE & 0xff000000 + orr r6, r6, #TCMIF_ASM_BASE & 0x00ff0000 + orr r6, r6, #TCMIF_ASM_BASE & 0x0000ff00 + + @ Prepare to put SDRAM into self-refresh manually + ldr r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + orr r9, r7, #SELF_REFRESH_MODE & 0xff000000 + orr r9, r9, #SELF_REFRESH_MODE & 0x000000ff + str r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + + @ Prepare to put EMIFS to Sleep + ldr r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + orr r9, r8, #IDLE_EMIFS_REQUEST & 0xff + str r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ Load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ Turn off clock domains + @ Do not disable PERCK (0x04) + mov r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff + orr r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ Request ARM idle + mov r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff + orr r3, r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff00 + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + +/* + * Let's wait for the next wake up event to wake us up. r0 can't be + * used here because r0 holds ARM_IDLECT1 + */ + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt + + @ Errata (HEL3SU467, section 1.4.4) specifies nop-instructions + @ according to this formula: + @ 2 + (4*DPLL_MULT)/DPLL_DIV/ARMDIV + @ Max DPLL_MULT = 18 + @ DPLL_DIV = 1 + @ ARMDIV = 1 + @ => 74 nop-instructions + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @10 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @20 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @30 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @40 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @50 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @60 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @70 + nop + nop + nop + nop @74 +/* + * omap1610_cpu_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack. + */ + @ Restore the ARM_IDLECT1 and ARM_IDLECT2. + strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ Restore EMIFF controls + str r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + str r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ Restore regs and return + ldmfd sp!, {r0 - r12, pc} + +ENTRY(omap1610_cpu_suspend_sz) + .word . - omap1610_cpu_suspend +#endif /* CONFIG_ARCH_OMAP16XX */ diff --git a/arch/arm/mach-omap1/soc.h b/arch/arm/mach-omap1/soc.h new file mode 100644 index 000000000..5fb57fdd9 --- /dev/null +++ b/arch/arm/mach-omap1/soc.h @@ -0,0 +1,6 @@ +/* + * We can move linux/soc/ti/omap1-soc.h here once the drivers are fixed + */ +#include "hardware.h" +#include "irqs.h" +#include <asm/irq.h> diff --git a/arch/arm/mach-omap1/sram-init.c b/arch/arm/mach-omap1/sram-init.c new file mode 100644 index 000000000..27c42e2a2 --- /dev/null +++ b/arch/arm/mach-omap1/sram-init.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP SRAM detection and management + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> + +#include <asm/fncpy.h> +#include <asm/tlb.h> +#include <asm/cacheflush.h> +#include <asm/set_memory.h> + +#include <asm/mach/map.h> + +#include "soc.h" +#include "sram.h" + +#define OMAP1_SRAM_PA 0x20000000 +#define SRAM_BOOTLOADER_SZ 0x80 +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) + +static void __iomem *omap_sram_base; +static unsigned long omap_sram_start; +static unsigned long omap_sram_skip; +static unsigned long omap_sram_size; +static void __iomem *omap_sram_ceil; + +/* + * Memory allocator for SRAM: calculates the new ceiling address + * for pushing a function using the fncpy API. + * + * Note that fncpy requires the returned address to be aligned + * to an 8-byte boundary. + */ +static void *omap_sram_push_address(unsigned long size) +{ + unsigned long available, new_ceil = (unsigned long)omap_sram_ceil; + + available = omap_sram_ceil - (omap_sram_base + omap_sram_skip); + + if (size > available) { + pr_err("Not enough space in SRAM\n"); + return NULL; + } + + new_ceil -= size; + new_ceil = ROUND_DOWN(new_ceil, FNCPY_ALIGN); + omap_sram_ceil = IOMEM(new_ceil); + + return (void __force *)omap_sram_ceil; +} + +void *omap_sram_push(void *funcp, unsigned long size) +{ + void *sram; + unsigned long base; + int pages; + void *dst = NULL; + + sram = omap_sram_push_address(size); + if (!sram) + return NULL; + + base = (unsigned long)sram & PAGE_MASK; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + set_memory_rw(base, pages); + + dst = fncpy(sram, funcp, size); + + set_memory_ro(base, pages); + set_memory_x(base, pages); + + return dst; +} + +/* + * The amount of SRAM depends on the core type. + * Note that we cannot try to test for SRAM here because writes + * to secure SRAM will hang the system. Also the SRAM is not + * yet mapped at this point. + * Note that we cannot use ioremap for SRAM, as clock init needs SRAM early. + */ +static void __init omap_detect_and_map_sram(void) +{ + unsigned long base; + int pages; + + omap_sram_skip = SRAM_BOOTLOADER_SZ; + omap_sram_start = OMAP1_SRAM_PA; + + if (cpu_is_omap7xx()) + omap_sram_size = 0x32000; /* 200K */ + else if (cpu_is_omap15xx()) + omap_sram_size = 0x30000; /* 192K */ + else if (cpu_is_omap1610() || cpu_is_omap1611() || + cpu_is_omap1621() || cpu_is_omap1710()) + omap_sram_size = 0x4000; /* 16K */ + else { + pr_err("Could not detect SRAM size\n"); + omap_sram_size = 0x4000; + } + + omap_sram_start = ROUND_DOWN(omap_sram_start, PAGE_SIZE); + omap_sram_base = __arm_ioremap_exec(omap_sram_start, omap_sram_size, 1); + if (!omap_sram_base) { + pr_err("SRAM: Could not map\n"); + return; + } + + omap_sram_ceil = omap_sram_base + omap_sram_size; + + /* + * Looks like we need to preserve some bootloader code at the + * beginning of SRAM for jumping to flash for reboot to work... + */ + memset_io(omap_sram_base + omap_sram_skip, 0, + omap_sram_size - omap_sram_skip); + + base = (unsigned long)omap_sram_base; + pages = PAGE_ALIGN(omap_sram_size) / PAGE_SIZE; + + set_memory_ro(base, pages); + set_memory_x(base, pages); +} + +static void (*_omap_sram_reprogram_clock)(u32 dpllctl, u32 ckctl); + +void omap_sram_reprogram_clock(u32 dpllctl, u32 ckctl) +{ + BUG_ON(!_omap_sram_reprogram_clock); + /* On 730, bit 13 must always be 1 */ + if (cpu_is_omap7xx()) + ckctl |= 0x2000; + _omap_sram_reprogram_clock(dpllctl, ckctl); +} + +int __init omap1_sram_init(void) +{ + omap_detect_and_map_sram(); + _omap_sram_reprogram_clock = + omap_sram_push(omap1_sram_reprogram_clock, + omap1_sram_reprogram_clock_sz); + + return 0; +} diff --git a/arch/arm/mach-omap1/sram.S b/arch/arm/mach-omap1/sram.S new file mode 100644 index 000000000..89f4dc1b7 --- /dev/null +++ b/arch/arm/mach-omap1/sram.S @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/plat-omap/sram-fn.S + * + * Functions that need to be run in internal SRAM + */ + +#include <linux/linkage.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/assembler.h> + +#include "hardware.h" +#include "iomap.h" + + .text + +/* + * Reprograms ULPD and CKCTL. + */ + .align 3 +ENTRY(omap1_sram_reprogram_clock) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + mov r2, #OMAP1_IO_ADDRESS(DPLL_CTL) & 0xff000000 + orr r2, r2, #OMAP1_IO_ADDRESS(DPLL_CTL) & 0x00ff0000 + orr r2, r2, #OMAP1_IO_ADDRESS(DPLL_CTL) & 0x0000ff00 + + mov r3, #OMAP1_IO_ADDRESS(ARM_CKCTL) & 0xff000000 + orr r3, r3, #OMAP1_IO_ADDRESS(ARM_CKCTL) & 0x00ff0000 + orr r3, r3, #OMAP1_IO_ADDRESS(ARM_CKCTL) & 0x0000ff00 + + tst r0, #1 << 4 @ want lock mode? + beq newck @ nope + bic r0, r0, #1 << 4 @ else clear lock bit + strh r0, [r2] @ set dpll into bypass mode + orr r0, r0, #1 << 4 @ set lock bit again + +newck: + strh r1, [r3] @ write new ckctl value + strh r0, [r2] @ write new dpll value + + mov r4, #0x0700 @ let the clocks settle + orr r4, r4, #0x00ff +delay: sub r4, r4, #1 + cmp r4, #0 + bne delay + +lock: ldrh r4, [r2], #0 @ read back dpll value + tst r0, #1 << 4 @ want lock mode? + beq out @ nope + tst r4, #1 << 0 @ dpll rate locked? + beq lock @ try again + +out: + ldmfd sp!, {r0 - r12, pc} @ restore regs and return +ENTRY(omap1_sram_reprogram_clock_sz) + .word . - omap1_sram_reprogram_clock diff --git a/arch/arm/mach-omap1/sram.h b/arch/arm/mach-omap1/sram.h new file mode 100644 index 000000000..f45e6dd6d --- /dev/null +++ b/arch/arm/mach-omap1/sram.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +extern void omap_sram_reprogram_clock(u32 dpllctl, u32 ckctl); + +int omap1_sram_init(void); +void *omap_sram_push(void *funcp, unsigned long size); + +/* Do not use these */ +extern void omap1_sram_reprogram_clock(u32 ckctl, u32 dpllctl); +extern unsigned long omap1_sram_reprogram_clock_sz; diff --git a/arch/arm/mach-omap1/tc.h b/arch/arm/mach-omap1/tc.h new file mode 100644 index 000000000..243454bc0 --- /dev/null +++ b/arch/arm/mach-omap1/tc.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * OMAP Traffic Controller + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + */ + +#ifndef __ASM_ARCH_TC_H +#define __ASM_ARCH_TC_H + +#define TCMIF_BASE 0xfffecc00 +#define OMAP_TC_OCPT1_PRIOR (TCMIF_BASE + 0x00) +#define OMAP_TC_EMIFS_PRIOR (TCMIF_BASE + 0x04) +#define OMAP_TC_EMIFF_PRIOR (TCMIF_BASE + 0x08) +#define EMIFS_CONFIG (TCMIF_BASE + 0x0c) +#define EMIFS_CS0_CONFIG (TCMIF_BASE + 0x10) +#define EMIFS_CS1_CONFIG (TCMIF_BASE + 0x14) +#define EMIFS_CS2_CONFIG (TCMIF_BASE + 0x18) +#define EMIFS_CS3_CONFIG (TCMIF_BASE + 0x1c) +#define EMIFF_SDRAM_CONFIG (TCMIF_BASE + 0x20) +#define EMIFF_MRS (TCMIF_BASE + 0x24) +#define TC_TIMEOUT1 (TCMIF_BASE + 0x28) +#define TC_TIMEOUT2 (TCMIF_BASE + 0x2c) +#define TC_TIMEOUT3 (TCMIF_BASE + 0x30) +#define TC_ENDIANISM (TCMIF_BASE + 0x34) +#define EMIFF_SDRAM_CONFIG_2 (TCMIF_BASE + 0x3c) +#define EMIF_CFG_DYNAMIC_WS (TCMIF_BASE + 0x40) +#define EMIFS_ACS0 (TCMIF_BASE + 0x50) +#define EMIFS_ACS1 (TCMIF_BASE + 0x54) +#define EMIFS_ACS2 (TCMIF_BASE + 0x58) +#define EMIFS_ACS3 (TCMIF_BASE + 0x5c) +#define OMAP_TC_OCPT2_PRIOR (TCMIF_BASE + 0xd0) + +/* external EMIFS chipselect regions */ +#define OMAP_CS0_PHYS 0x00000000 +#define OMAP_CS0_SIZE SZ_64M + +#define OMAP_CS1_PHYS 0x04000000 +#define OMAP_CS1_SIZE SZ_64M + +#define OMAP_CS1A_PHYS OMAP_CS1_PHYS +#define OMAP_CS1A_SIZE SZ_32M + +#define OMAP_CS1B_PHYS (OMAP_CS1A_PHYS + OMAP_CS1A_SIZE) +#define OMAP_CS1B_SIZE SZ_32M + +#define OMAP_CS2_PHYS 0x08000000 +#define OMAP_CS2_SIZE SZ_64M + +#define OMAP_CS2A_PHYS OMAP_CS2_PHYS +#define OMAP_CS2A_SIZE SZ_32M + +#define OMAP_CS2B_PHYS (OMAP_CS2A_PHYS + OMAP_CS2A_SIZE) +#define OMAP_CS2B_SIZE SZ_32M + +#define OMAP_CS3_PHYS 0x0c000000 +#define OMAP_CS3_SIZE SZ_64M + +#ifndef __ASSEMBLER__ + +/* EMIF Slow Interface Configuration Register */ +#define OMAP_EMIFS_CONFIG_FR (1 << 4) +#define OMAP_EMIFS_CONFIG_PDE (1 << 3) +#define OMAP_EMIFS_CONFIG_PWD_EN (1 << 2) +#define OMAP_EMIFS_CONFIG_BM (1 << 1) +#define OMAP_EMIFS_CONFIG_WP (1 << 0) + +#define EMIFS_CCS(n) (EMIFS_CS0_CONFIG + (4 * (n))) +#define EMIFS_ACS(n) (EMIFS_ACS0 + (4 * (n))) + +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_ARCH_TC_H */ diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c new file mode 100644 index 000000000..d5e127851 --- /dev/null +++ b/arch/arm/mach-omap1/time.c @@ -0,0 +1,234 @@ +/* + * linux/arch/arm/mach-omap1/time.c + * + * OMAP Timers + * + * Copyright (C) 2004 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen <tony@atomide.com> and + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/io.h> +#include <linux/sched_clock.h> + +#include <asm/irq.h> + +#include <asm/mach/irq.h> +#include <asm/mach/time.h> + +#include "hardware.h" +#include "mux.h" +#include "iomap.h" +#include "common.h" +#include "clock.h" + +#ifdef CONFIG_OMAP_MPU_TIMER + +#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE +#define OMAP_MPU_TIMER_OFFSET 0x100 + +typedef struct { + u32 cntl; /* CNTL_TIMER, R/W */ + u32 load_tim; /* LOAD_TIM, W */ + u32 read_tim; /* READ_TIM, R */ +} omap_mpu_timer_regs_t; + +#define omap_mpu_timer_base(n) \ +((omap_mpu_timer_regs_t __iomem *)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ + (n)*OMAP_MPU_TIMER_OFFSET)) + +static inline unsigned long notrace omap_mpu_timer_read(int nr) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); + return readl(&timer->read_tim); +} + +static inline void omap_mpu_set_autoreset(int nr) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); + + writel(readl(&timer->cntl) | MPU_TIMER_AR, &timer->cntl); +} + +static inline void omap_mpu_remove_autoreset(int nr) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); + + writel(readl(&timer->cntl) & ~MPU_TIMER_AR, &timer->cntl); +} + +static inline void omap_mpu_timer_start(int nr, unsigned long load_val, + int autoreset) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); + unsigned int timerflags = MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST; + + if (autoreset) + timerflags |= MPU_TIMER_AR; + + writel(MPU_TIMER_CLOCK_ENABLE, &timer->cntl); + udelay(1); + writel(load_val, &timer->load_tim); + udelay(1); + writel(timerflags, &timer->cntl); +} + +static inline void omap_mpu_timer_stop(int nr) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); + + writel(readl(&timer->cntl) & ~MPU_TIMER_ST, &timer->cntl); +} + +/* + * --------------------------------------------------------------------------- + * MPU timer 1 ... count down to zero, interrupt, reload + * --------------------------------------------------------------------------- + */ +static int omap_mpu_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + omap_mpu_timer_start(0, cycles, 0); + return 0; +} + +static int omap_mpu_set_oneshot(struct clock_event_device *evt) +{ + omap_mpu_timer_stop(0); + omap_mpu_remove_autoreset(0); + return 0; +} + +static int omap_mpu_set_periodic(struct clock_event_device *evt) +{ + omap_mpu_set_autoreset(0); + return 0; +} + +static struct clock_event_device clockevent_mpu_timer1 = { + .name = "mpu_timer1", + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = omap_mpu_set_next_event, + .set_state_periodic = omap_mpu_set_periodic, + .set_state_oneshot = omap_mpu_set_oneshot, +}; + +static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &clockevent_mpu_timer1; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static __init void omap_init_mpu_timer(unsigned long rate) +{ + if (request_irq(INT_TIMER1, omap_mpu_timer1_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "mpu_timer1", NULL)) + pr_err("Failed to request irq %d (mpu_timer1)\n", INT_TIMER1); + omap_mpu_timer_start(0, (rate / HZ) - 1, 1); + + clockevent_mpu_timer1.cpumask = cpumask_of(0); + clockevents_config_and_register(&clockevent_mpu_timer1, rate, + 1, -1); +} + + +/* + * --------------------------------------------------------------------------- + * MPU timer 2 ... free running 32-bit clock source and scheduler clock + * --------------------------------------------------------------------------- + */ + +static u64 notrace omap_mpu_read_sched_clock(void) +{ + return ~omap_mpu_timer_read(1); +} + +static void __init omap_init_clocksource(unsigned long rate) +{ + omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(1); + static char err[] __initdata = KERN_ERR + "%s: can't register clocksource!\n"; + + omap_mpu_timer_start(1, ~0, 1); + sched_clock_register(omap_mpu_read_sched_clock, 32, rate); + + if (clocksource_mmio_init(&timer->read_tim, "mpu_timer2", rate, + 300, 32, clocksource_mmio_readl_down)) + printk(err, "mpu_timer2"); +} + +static void __init omap_mpu_timer_init(void) +{ + struct clk *ck_ref = clk_get(NULL, "ck_ref"); + unsigned long rate; + + BUG_ON(IS_ERR(ck_ref)); + + rate = clk_get_rate(ck_ref); + clk_put(ck_ref); + + /* PTV = 0 */ + rate /= 2; + + omap_init_mpu_timer(rate); + omap_init_clocksource(rate); +} + +#else +static inline void omap_mpu_timer_init(void) +{ + pr_err("Bogus timer, should not happen\n"); +} +#endif /* CONFIG_OMAP_MPU_TIMER */ + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +void __init omap1_timer_init(void) +{ + omap1_clk_init(); + omap1_mux_init(); + + if (omap_32k_timer_init() != 0) + omap_mpu_timer_init(); +} diff --git a/arch/arm/mach-omap1/timer.c b/arch/arm/mach-omap1/timer.c new file mode 100644 index 000000000..81a912c11 --- /dev/null +++ b/arch/arm/mach-omap1/timer.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP1 Dual-Mode Timers - platform device registration + * + * Contains first level initialization routines which internally + * generates timer device information and registers with linux + * device model. It also has a low level function to change the timer + * input clock source. + * + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ + * Tarun Kanti DebBarma <tarun.kanti@ti.com> + * Thara Gopinath <thara@ti.com> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/platform_data/dmtimer-omap.h> +#include <linux/soc/ti/omap1-io.h> + +#include <clocksource/timer-ti-dm.h> + +#include "soc.h" + +#define OMAP1610_GPTIMER1_BASE 0xfffb1400 +#define OMAP1610_GPTIMER2_BASE 0xfffb1c00 +#define OMAP1610_GPTIMER3_BASE 0xfffb2400 +#define OMAP1610_GPTIMER4_BASE 0xfffb2c00 +#define OMAP1610_GPTIMER5_BASE 0xfffb3400 +#define OMAP1610_GPTIMER6_BASE 0xfffb3c00 +#define OMAP1610_GPTIMER7_BASE 0xfffb7400 +#define OMAP1610_GPTIMER8_BASE 0xfffbd400 + +#define OMAP1_DM_TIMER_COUNT 8 + +static int omap1_dm_timer_set_src(struct platform_device *pdev, + int source) +{ + int n = (pdev->id - 1) << 1; + u32 l; + + l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); + l |= source << n; + omap_writel(l, MOD_CONF_CTRL_1); + + return 0; +} + +static int __init omap1_dm_timer_init(void) +{ + int i; + int ret; + struct dmtimer_platform_data *pdata; + struct platform_device *pdev; + + if (!cpu_is_omap16xx()) + return 0; + + for (i = 1; i <= OMAP1_DM_TIMER_COUNT; i++) { + struct resource res[2]; + u32 base, irq; + + switch (i) { + case 1: + base = OMAP1610_GPTIMER1_BASE; + irq = INT_1610_GPTIMER1; + break; + case 2: + base = OMAP1610_GPTIMER2_BASE; + irq = INT_1610_GPTIMER2; + break; + case 3: + base = OMAP1610_GPTIMER3_BASE; + irq = INT_1610_GPTIMER3; + break; + case 4: + base = OMAP1610_GPTIMER4_BASE; + irq = INT_1610_GPTIMER4; + break; + case 5: + base = OMAP1610_GPTIMER5_BASE; + irq = INT_1610_GPTIMER5; + break; + case 6: + base = OMAP1610_GPTIMER6_BASE; + irq = INT_1610_GPTIMER6; + break; + case 7: + base = OMAP1610_GPTIMER7_BASE; + irq = INT_1610_GPTIMER7; + break; + case 8: + base = OMAP1610_GPTIMER8_BASE; + irq = INT_1610_GPTIMER8; + break; + default: + /* + * not supposed to reach here. + * this is to remove warning. + */ + return -EINVAL; + } + + pdev = platform_device_alloc("omap_timer", i); + if (!pdev) { + pr_err("%s: Failed to device alloc for dmtimer%d\n", + __func__, i); + return -ENOMEM; + } + + memset(res, 0, 2 * sizeof(struct resource)); + res[0].start = base; + res[0].end = base + 0x46; + res[0].flags = IORESOURCE_MEM; + res[1].start = irq; + res[1].end = irq; + res[1].flags = IORESOURCE_IRQ; + ret = platform_device_add_resources(pdev, res, + ARRAY_SIZE(res)); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to add resources.\n", + __func__); + goto err_free_pdev; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_free_pdata; + } + + pdata->set_timer_src = omap1_dm_timer_set_src; + pdata->timer_capability = OMAP_TIMER_ALWON | + OMAP_TIMER_NEEDS_RESET | OMAP_TIMER_HAS_DSP_IRQ; + + ret = platform_device_add_data(pdev, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to add platform data.\n", + __func__); + goto err_free_pdata; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to add platform device.\n", + __func__); + goto err_free_pdata; + } + + dev_dbg(&pdev->dev, " Registered.\n"); + } + + return 0; + +err_free_pdata: + kfree(pdata); + +err_free_pdev: + platform_device_put(pdev); + + return ret; +} +arch_initcall(omap1_dm_timer_init); diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c new file mode 100644 index 000000000..f618a6df2 --- /dev/null +++ b/arch/arm/mach-omap1/timer32k.c @@ -0,0 +1,282 @@ +/* + * linux/arch/arm/mach-omap1/timer32k.c + * + * OMAP 32K Timer + * + * Copyright (C) 2004 - 2005 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen <tony@atomide.com> and + * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * OMAP Dual-mode timer framework support by Timo Teras + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon <glonnon@ridgerun.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/io.h> +#include <linux/sched_clock.h> + +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/mach/time.h> + +#include "hardware.h" +#include "common.h" + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ + +/* 16xx specific defines */ +#define OMAP1_32K_TIMER_BASE 0xfffb9000 +#define OMAP1_32KSYNC_TIMER_BASE 0xfffbc400 +#define OMAP1_32K_TIMER_CR 0x08 +#define OMAP1_32K_TIMER_TVR 0x00 +#define OMAP1_32K_TIMER_TCR 0x04 + +#define OMAP_32K_TICKS_PER_SEC (32768) + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 128, TVR = 255. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1) + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + omap_writew(val, OMAP1_32K_TIMER_BASE + reg); +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + if (!load_val) + load_val = 1; + omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); +} + +static inline void omap_32k_timer_stop(void) +{ + omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); +} + +#define omap_32k_timer_ack_irq() + +static int omap_32k_timer_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + omap_32k_timer_start(delta); + + return 0; +} + +static int omap_32k_timer_shutdown(struct clock_event_device *evt) +{ + omap_32k_timer_stop(); + return 0; +} + +static int omap_32k_timer_set_periodic(struct clock_event_device *evt) +{ + omap_32k_timer_stop(); + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + return 0; +} + +static struct clock_event_device clockevent_32k_timer = { + .name = "32k-timer", + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = omap_32k_timer_set_next_event, + .set_state_shutdown = omap_32k_timer_shutdown, + .set_state_periodic = omap_32k_timer_set_periodic, + .set_state_oneshot = omap_32k_timer_shutdown, + .tick_resume = omap_32k_timer_shutdown, +}; + +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &clockevent_32k_timer; + omap_32k_timer_ack_irq(); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static __init void omap_init_32k_timer(void) +{ + if (request_irq(INT_OS_TIMER, omap_32k_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "32KHz timer", NULL)) + pr_err("Failed to request irq %d(32KHz timer)\n", INT_OS_TIMER); + + clockevent_32k_timer.cpumask = cpumask_of(0); + clockevents_config_and_register(&clockevent_32k_timer, + OMAP_32K_TICKS_PER_SEC, 1, 0xfffffffe); +} + +/* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */ +#define OMAP2_32KSYNCNT_REV_OFF 0x0 +#define OMAP2_32KSYNCNT_REV_SCHEME (0x3 << 30) +#define OMAP2_32KSYNCNT_CR_OFF_LOW 0x10 +#define OMAP2_32KSYNCNT_CR_OFF_HIGH 0x30 + +/* + * 32KHz clocksource ... always available, on pretty most chips except + * OMAP 730 and 1510. Other timers could be used as clocksources, with + * higher resolution in free-running counter modes (e.g. 12 MHz xtal), + * but systems won't necessarily want to spend resources that way. + */ +static void __iomem *sync32k_cnt_reg; + +static u64 notrace omap_32k_read_sched_clock(void) +{ + return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0; +} + +static struct timespec64 persistent_ts; +static cycles_t cycles; +static unsigned int persistent_mult, persistent_shift; + +/** + * omap_read_persistent_clock64 - Return time from a persistent clock. + * @ts: &struct timespec64 for the returned time + * + * Reads the time from a source which isn't disabled during PM, the + * 32k sync timer. Convert the cycles elapsed since last read into + * nsecs and adds to a monotonically increasing timespec64. + */ +static void omap_read_persistent_clock64(struct timespec64 *ts) +{ + unsigned long long nsecs; + cycles_t last_cycles; + + last_cycles = cycles; + cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0; + + nsecs = clocksource_cyc2ns(cycles - last_cycles, + persistent_mult, persistent_shift); + + timespec64_add_ns(&persistent_ts, nsecs); + + *ts = persistent_ts; +} + +/** + * omap_init_clocksource_32k - setup and register counter 32k as a + * kernel clocksource + * @vbase: base addr of counter_32k module + * + * Returns: %0 upon success or negative error code upon failure. + * + */ +static int __init omap_init_clocksource_32k(void __iomem *vbase) +{ + int ret; + + /* + * 32k sync Counter IP register offsets vary between the + * highlander version and the legacy ones. + * The 'SCHEME' bits(30-31) of the revision register is used + * to identify the version. + */ + if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) & + OMAP2_32KSYNCNT_REV_SCHEME) + sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH; + else + sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW; + + /* + * 120000 rough estimate from the calculations in + * __clocksource_update_freq_scale. + */ + clocks_calc_mult_shift(&persistent_mult, &persistent_shift, + 32768, NSEC_PER_SEC, 120000); + + ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768, + 250, 32, clocksource_mmio_readl_up); + if (ret) { + pr_err("32k_counter: can't register clocksource\n"); + return ret; + } + + sched_clock_register(omap_32k_read_sched_clock, 32, 32768); + register_persistent_clock(omap_read_persistent_clock64); + pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n"); + + return 0; +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +int __init omap_32k_timer_init(void) +{ + int ret = -ENODEV; + + if (cpu_is_omap16xx()) { + void __iomem *base; + struct clk *sync32k_ick; + + base = ioremap(OMAP1_32KSYNC_TIMER_BASE, SZ_1K); + if (!base) { + pr_err("32k_counter: failed to map base addr\n"); + return -ENODEV; + } + + sync32k_ick = clk_get(NULL, "omap_32ksync_ick"); + if (!IS_ERR(sync32k_ick)) + clk_prepare_enable(sync32k_ick); + + ret = omap_init_clocksource_32k(base); + } + + if (!ret) + omap_init_32k_timer(); + + return ret; +} diff --git a/arch/arm/mach-omap1/usb.c b/arch/arm/mach-omap1/usb.c new file mode 100644 index 000000000..0119f3ddb --- /dev/null +++ b/arch/arm/mach-omap1/usb.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Platform level USB initialization for FS USB OTG controller on omap1 + * + * Copyright (C) 2004 Texas Instruments, Inc. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/dma-map-ops.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/soc/ti/omap1-io.h> + +#include <asm/irq.h> + +#include "hardware.h" +#include "mux.h" +#include "usb.h" +#include "common.h" + +/* These routines should handle the standard chip-specific modes + * for usb0/1/2 ports, covering basic mux and transceiver setup. + * + * Some board-*.c files will need to set up additional mux options, + * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. + */ + +/* TESTED ON: + * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables + * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables + * - 5912 OSK UDC, with *nonstandard* A-to-A cable + * - 1510 Innovator UDC with bundled usb0 cable + * - 1510 Innovator OHCI with bundled usb1/usb2 cable + * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS + * - 1710 custom development board using alternate pin group + * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables + */ + +#define INT_USB_IRQ_GEN IH2_BASE + 20 +#define INT_USB_IRQ_NISO IH2_BASE + 30 +#define INT_USB_IRQ_ISO IH2_BASE + 29 +#define INT_USB_IRQ_HGEN INT_USB_HHC_1 +#define INT_USB_IRQ_OTG IH2_BASE + 8 + +#ifdef CONFIG_ARCH_OMAP_OTG + +static void __init +omap_otg_init(struct omap_usb_config *config) +{ + u32 syscon; + int alt_pingroup = 0; + u16 w; + + /* NOTE: no bus or clock setup (yet?) */ + + syscon = omap_readl(OTG_SYSCON_1) & 0xffff; + if (!(syscon & OTG_RESET_DONE)) + pr_debug("USB resets not complete?\n"); + + //omap_writew(0, OTG_IRQ_EN); + + /* pin muxing and transceiver pinouts */ + if (config->pins[0] > 2) /* alt pingroup 2 */ + alt_pingroup = 1; + syscon |= config->usb0_init(config->pins[0], is_usb0_device(config)); + syscon |= config->usb1_init(config->pins[1]); + syscon |= config->usb2_init(config->pins[2], alt_pingroup); + pr_debug("OTG_SYSCON_1 = %08x\n", omap_readl(OTG_SYSCON_1)); + omap_writel(syscon, OTG_SYSCON_1); + + syscon = config->hmc_mode; + syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; +#ifdef CONFIG_USB_OTG + if (config->otg) + syscon |= OTG_EN; +#endif + pr_debug("USB_TRANSCEIVER_CTRL = %03x\n", + omap_readl(USB_TRANSCEIVER_CTRL)); + pr_debug("OTG_SYSCON_2 = %08x\n", omap_readl(OTG_SYSCON_2)); + omap_writel(syscon, OTG_SYSCON_2); + + printk("USB: hmc %d", config->hmc_mode); + if (!alt_pingroup) + pr_cont(", usb2 alt %d wires", config->pins[2]); + else if (config->pins[0]) + pr_cont(", usb0 %d wires%s", config->pins[0], + is_usb0_device(config) ? " (dev)" : ""); + if (config->pins[1]) + pr_cont(", usb1 %d wires", config->pins[1]); + if (!alt_pingroup && config->pins[2]) + pr_cont(", usb2 %d wires", config->pins[2]); + if (config->otg) + pr_cont(", Mini-AB on usb%d", config->otg - 1); + pr_cont("\n"); + + /* leave USB clocks/controllers off until needed */ + w = omap_readw(ULPD_SOFT_REQ); + w &= ~SOFT_USB_CLK_REQ; + omap_writew(w, ULPD_SOFT_REQ); + + w = omap_readw(ULPD_CLOCK_CTRL); + w &= ~USB_MCLK_EN; + w |= DIS_USB_PVCI_CLK; + omap_writew(w, ULPD_CLOCK_CTRL); + + syscon = omap_readl(OTG_SYSCON_1); + syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; + +#if IS_ENABLED(CONFIG_USB_OMAP) + if (config->otg || config->register_dev) { + struct platform_device *udc_device = config->udc_device; + int status; + + syscon &= ~DEV_IDLE_EN; + udc_device->dev.platform_data = config; + status = platform_device_register(udc_device); + if (status) + pr_debug("can't register UDC device, %d\n", status); + } +#endif + +#if IS_ENABLED(CONFIG_USB_OHCI_HCD) + if (config->otg || config->register_host) { + struct platform_device *ohci_device = config->ohci_device; + int status; + + syscon &= ~HST_IDLE_EN; + ohci_device->dev.platform_data = config; + status = platform_device_register(ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + } +#endif + +#ifdef CONFIG_USB_OTG + if (config->otg) { + struct platform_device *otg_device = config->otg_device; + int status; + + syscon &= ~OTG_IDLE_EN; + otg_device->dev.platform_data = config; + status = platform_device_register(otg_device); + if (status) + pr_debug("can't register OTG device, %d\n", status); + } +#endif + pr_debug("OTG_SYSCON_1 = %08x\n", omap_readl(OTG_SYSCON_1)); + omap_writel(syscon, OTG_SYSCON_1); +} + +#else +static void omap_otg_init(struct omap_usb_config *config) {} +#endif + +#if IS_ENABLED(CONFIG_USB_OMAP) + +static struct resource udc_resources[] = { + /* order is significant! */ + { /* registers */ + .start = UDC_BASE, + .end = UDC_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, { /* general IRQ */ + .start = INT_USB_IRQ_GEN, + .flags = IORESOURCE_IRQ, + }, { /* PIO IRQ */ + .start = INT_USB_IRQ_NISO, + .flags = IORESOURCE_IRQ, + }, { /* SOF IRQ */ + .start = INT_USB_IRQ_ISO, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dmamask = ~(u32)0; + +static struct platform_device udc_device = { + .name = "omap_udc", + .id = -1, + .dev = { + .dma_mask = &udc_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(udc_resources), + .resource = udc_resources, +}; + +static inline void udc_device_init(struct omap_usb_config *pdata) +{ + /* IRQ numbers for omap7xx */ + if(cpu_is_omap7xx()) { + udc_resources[1].start = INT_7XX_USB_GENI; + udc_resources[2].start = INT_7XX_USB_NON_ISO; + udc_resources[3].start = INT_7XX_USB_ISO; + } + pdata->udc_device = &udc_device; +} + +#else + +static inline void udc_device_init(struct omap_usb_config *pdata) +{ +} + +#endif + +/* The dmamask must be set for OHCI to work */ +static u64 ohci_dmamask = ~(u32)0; + +static struct resource ohci_resources[] = { + { + .start = OMAP_OHCI_BASE, + .end = OMAP_OHCI_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_IRQ_HGEN, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci_device = { + .name = "ohci", + .id = -1, + .dev = { + .dma_mask = &ohci_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(ohci_resources), + .resource = ohci_resources, +}; + +static inline void ohci_device_init(struct omap_usb_config *pdata) +{ + if (!IS_ENABLED(CONFIG_USB_OHCI_HCD)) + return; + + if (cpu_is_omap7xx()) + ohci_resources[1].start = INT_7XX_USB_HHC_1; + pdata->ohci_device = &ohci_device; + pdata->ocpi_enable = &ocpi_enable; +} + +#if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) + +static struct resource otg_resources[] = { + /* order is significant! */ + { + .start = OTG_BASE, + .end = OTG_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, { + .start = INT_USB_IRQ_OTG, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device otg_device = { + .name = "omap_otg", + .id = -1, + .num_resources = ARRAY_SIZE(otg_resources), + .resource = otg_resources, +}; + +static inline void otg_device_init(struct omap_usb_config *pdata) +{ + if (cpu_is_omap7xx()) + otg_resources[1].start = INT_7XX_USB_OTG; + pdata->otg_device = &otg_device; +} + +#else + +static inline void otg_device_init(struct omap_usb_config *pdata) +{ +} + +#endif + +static u32 __init omap1_usb0_init(unsigned nwires, unsigned is_device) +{ + u32 syscon1 = 0; + + if (nwires == 0) { + if (!cpu_is_omap15xx()) { + u32 l; + + /* pulldown D+/D- */ + l = omap_readl(USB_TRANSCEIVER_CTRL); + l &= ~(3 << 1); + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + return 0; + } + + if (is_device) { + if (cpu_is_omap7xx()) { + omap_cfg_reg(AA17_7XX_USB_DM); + omap_cfg_reg(W16_7XX_USB_PU_EN); + omap_cfg_reg(W17_7XX_USB_VBUSI); + omap_cfg_reg(W18_7XX_USB_DMCK_OUT); + omap_cfg_reg(W19_7XX_USB_DCRST); + } else + omap_cfg_reg(W4_USB_PUEN); + } + + if (nwires == 2) { + u32 l; + + // omap_cfg_reg(P9_USB_DP); + // omap_cfg_reg(R8_USB_DM); + + if (cpu_is_omap15xx()) { + /* This works on 1510-Innovator */ + return 0; + } + + /* NOTES: + * - peripheral should configure VBUS detection! + * - only peripherals may use the internal D+/D- pulldowns + * - OTG support on this port not yet written + */ + + /* Don't do this for omap7xx -- it causes USB to not work correctly */ + if (!cpu_is_omap7xx()) { + l = omap_readl(USB_TRANSCEIVER_CTRL); + l &= ~(7 << 4); + if (!is_device) + l |= (3 << 1); + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + + return 3 << 16; + } + + /* alternate pin config, external transceiver */ + if (cpu_is_omap15xx()) { + printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); + return 0; + } + + omap_cfg_reg(V6_USB0_TXD); + omap_cfg_reg(W9_USB0_TXEN); + omap_cfg_reg(W5_USB0_SE0); + if (nwires != 3) + omap_cfg_reg(Y5_USB0_RCV); + + /* NOTE: SPEED and SUSP aren't configured here. OTG hosts + * may be able to use I2C requests to set those bits along + * with VBUS switching and overcurrent detection. + */ + + if (nwires != 6) { + u32 l; + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l &= ~CONF_USB2_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + { + u32 l; + + omap_cfg_reg(AA9_USB0_VP); + omap_cfg_reg(R9_USB0_VM); + l = omap_readl(USB_TRANSCEIVER_CTRL); + l |= CONF_USB2_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 0, nwires); + } + + return syscon1 << 16; +} + +static u32 __init omap1_usb1_init(unsigned nwires) +{ + u32 syscon1 = 0; + + if (!cpu_is_omap15xx() && nwires != 6) { + u32 l; + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l &= ~CONF_USB1_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + if (nwires == 0) + return 0; + + /* external transceiver */ + omap_cfg_reg(USB1_TXD); + omap_cfg_reg(USB1_TXEN); + if (nwires != 3) + omap_cfg_reg(USB1_RCV); + + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB1_SEO); + omap_cfg_reg(USB1_SPEED); + // SUSP + } else if (cpu_is_omap1610() || cpu_is_omap5912()) { + omap_cfg_reg(W13_1610_USB1_SE0); + omap_cfg_reg(R13_1610_USB1_SPEED); + // SUSP + } else if (cpu_is_omap1710()) { + omap_cfg_reg(R13_1710_USB1_SE0); + // SUSP + } else { + pr_debug("usb%d cpu unrecognized\n", 1); + return 0; + } + + switch (nwires) { + case 2: + goto bad; + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + omap_cfg_reg(USB1_VP); + omap_cfg_reg(USB1_VM); + if (!cpu_is_omap15xx()) { + u32 l; + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l |= CONF_USB1_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + break; + default: +bad: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 1, nwires); + } + + return syscon1 << 20; +} + +static u32 __init omap1_usb2_init(unsigned nwires, unsigned alt_pingroup) +{ + u32 syscon1 = 0; + + /* NOTE omap1 erratum: must leave USB2_UNI_R set if usb0 in use */ + if (alt_pingroup || nwires == 0) + return 0; + + if (!cpu_is_omap15xx() && nwires != 6) { + u32 l; + + l = omap_readl(USB_TRANSCEIVER_CTRL); + l &= ~CONF_USB2_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + + /* external transceiver */ + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB2_TXD); + omap_cfg_reg(USB2_TXEN); + omap_cfg_reg(USB2_SEO); + if (nwires != 3) + omap_cfg_reg(USB2_RCV); + /* there is no USB2_SPEED */ + } else if (cpu_is_omap16xx()) { + omap_cfg_reg(V6_USB2_TXD); + omap_cfg_reg(W9_USB2_TXEN); + omap_cfg_reg(W5_USB2_SE0); + if (nwires != 3) + omap_cfg_reg(Y5_USB2_RCV); + // FIXME omap_cfg_reg(USB2_SPEED); + } else { + pr_debug("usb%d cpu unrecognized\n", 1); + return 0; + } + + // omap_cfg_reg(USB2_SUSP); + + switch (nwires) { + case 2: + goto bad; + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 5: + goto bad; + case 6: + syscon1 = 3; + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB2_VP); + omap_cfg_reg(USB2_VM); + } else { + u32 l; + + omap_cfg_reg(AA9_USB2_VP); + omap_cfg_reg(R9_USB2_VM); + l = omap_readl(USB_TRANSCEIVER_CTRL); + l |= CONF_USB2_UNI_R; + omap_writel(l, USB_TRANSCEIVER_CTRL); + } + break; + default: +bad: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 2, nwires); + } + + return syscon1 << 24; +} + +#ifdef CONFIG_ARCH_OMAP15XX +/* OMAP-1510 OHCI has its own MMU for DMA */ +#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */ +#define OMAP1510_LB_CLOCK_DIV 0xfffec10c +#define OMAP1510_LB_MMU_CTL 0xfffec208 +#define OMAP1510_LB_MMU_LCK 0xfffec224 +#define OMAP1510_LB_MMU_LD_TLB 0xfffec228 +#define OMAP1510_LB_MMU_CAM_H 0xfffec22c +#define OMAP1510_LB_MMU_CAM_L 0xfffec230 +#define OMAP1510_LB_MMU_RAM_H 0xfffec234 +#define OMAP1510_LB_MMU_RAM_L 0xfffec238 + +/* + * Bus address is physical address, except for OMAP-1510 Local Bus. + * OMAP-1510 bus address is translated into a Local Bus address if the + * OMAP bus type is lbus. + */ +#define OMAP1510_LB_OFFSET UL(0x30000000) + +/* + * OMAP-1510 specific Local Bus clock on/off + */ +static int omap_1510_local_bus_power(int on) +{ + if (on) { + omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); + udelay(200); + } else { + omap_writel(0, OMAP1510_LB_MMU_CTL); + } + + return 0; +} + +/* + * OMAP-1510 specific Local Bus initialization + * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. + * See also arch/mach-omap/memory.h for __virt_to_dma() and + * __dma_to_virt() which need to match with the physical + * Local Bus address below. + */ +static int omap_1510_local_bus_init(void) +{ + unsigned int tlb; + unsigned long lbaddr, physaddr; + + omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, + OMAP1510_LB_CLOCK_DIV); + + /* Configure the Local Bus MMU table */ + for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) { + lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; + physaddr = tlb * 0x00100000 + PHYS_OFFSET; + omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); + omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, + OMAP1510_LB_MMU_CAM_L); + omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); + omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); + omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK); + omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB); + } + + /* Enable the walking table */ + omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); + udelay(200); + + return 0; +} + +static void omap_1510_local_bus_reset(void) +{ + omap_1510_local_bus_power(1); + omap_1510_local_bus_init(); +} + +/* ULPD_DPLL_CTRL */ +#define DPLL_IOB (1 << 13) +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_LOCK (1 << 0) + +/* ULPD_APLL_CTRL */ +#define APLL_NDPLL_SWITCH (1 << 0) + +static void __init omap_1510_usb_init(struct omap_usb_config *config) +{ + unsigned int val; + u16 w; + + config->usb0_init(config->pins[0], is_usb0_device(config)); + config->usb1_init(config->pins[1]); + config->usb2_init(config->pins[2], 0); + + val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); + val |= (config->hmc_mode << 1); + omap_writel(val, MOD_CONF_CTRL_0); + + printk("USB: hmc %d", config->hmc_mode); + if (config->pins[0]) + pr_cont(", usb0 %d wires%s", config->pins[0], + is_usb0_device(config) ? " (dev)" : ""); + if (config->pins[1]) + pr_cont(", usb1 %d wires", config->pins[1]); + if (config->pins[2]) + pr_cont(", usb2 %d wires", config->pins[2]); + pr_cont("\n"); + + /* use DPLL for 48 MHz function clock */ + pr_debug("APLL %04x DPLL %04x REQ %04x\n", omap_readw(ULPD_APLL_CTRL), + omap_readw(ULPD_DPLL_CTRL), omap_readw(ULPD_SOFT_REQ)); + + w = omap_readw(ULPD_APLL_CTRL); + w &= ~APLL_NDPLL_SWITCH; + omap_writew(w, ULPD_APLL_CTRL); + + w = omap_readw(ULPD_DPLL_CTRL); + w |= DPLL_IOB | DPLL_PLL_ENABLE; + omap_writew(w, ULPD_DPLL_CTRL); + + w = omap_readw(ULPD_SOFT_REQ); + w |= SOFT_UDC_REQ | SOFT_DPLL_REQ; + omap_writew(w, ULPD_SOFT_REQ); + + while (!(omap_readw(ULPD_DPLL_CTRL) & DPLL_LOCK)) + cpu_relax(); + +#if IS_ENABLED(CONFIG_USB_OMAP) + if (config->register_dev) { + int status; + + udc_device.dev.platform_data = config; + status = platform_device_register(&udc_device); + if (status) + pr_debug("can't register UDC device, %d\n", status); + /* udc driver gates 48MHz by D+ pullup */ + } +#endif + + if (IS_ENABLED(CONFIG_USB_OHCI_HCD) && config->register_host) { + int status; + + ohci_device.dev.platform_data = config; + dma_direct_set_offset(&ohci_device.dev, PHYS_OFFSET, + OMAP1510_LB_OFFSET, (u64)-1); + status = platform_device_register(&ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + /* hcd explicitly gates 48MHz */ + + config->lb_reset = omap_1510_local_bus_reset; + } +} + +#else +static inline void omap_1510_usb_init(struct omap_usb_config *config) {} +#endif + +void __init omap1_usb_init(struct omap_usb_config *_pdata) +{ + struct omap_usb_config *pdata; + + pdata = kmemdup(_pdata, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return; + + pdata->usb0_init = omap1_usb0_init; + pdata->usb1_init = omap1_usb1_init; + pdata->usb2_init = omap1_usb2_init; + udc_device_init(pdata); + ohci_device_init(pdata); + otg_device_init(pdata); + + if (cpu_is_omap7xx() || cpu_is_omap16xx()) + omap_otg_init(pdata); + else if (cpu_is_omap15xx()) + omap_1510_usb_init(pdata); + else + printk(KERN_ERR "USB: No init for your chip yet\n"); +} diff --git a/arch/arm/mach-omap1/usb.h b/arch/arm/mach-omap1/usb.h new file mode 100644 index 000000000..08c9344c4 --- /dev/null +++ b/arch/arm/mach-omap1/usb.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * fixme correct answer depends on hmc_mode, + * as does (on omap1) any nonzero value for config->otg port number + */ +#include <linux/platform_data/usb-omap1.h> +#include <linux/soc/ti/omap1-usb.h> + +#if IS_ENABLED(CONFIG_USB_OMAP) +#define is_usb0_device(config) 1 +#else +#define is_usb0_device(config) 0 +#endif + +#if IS_ENABLED(CONFIG_USB_SUPPORT) +void omap1_usb_init(struct omap_usb_config *pdata); +#else +static inline void omap1_usb_init(struct omap_usb_config *pdata) +{ +} +#endif + +#define OMAP1_OHCI_BASE 0xfffba000 +#define OMAP2_OHCI_BASE 0x4805e000 +#define OMAP_OHCI_BASE OMAP1_OHCI_BASE |