diff options
Diffstat (limited to 'arch')
399 files changed, 35554 insertions, 9285 deletions
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 01b10ab588a..5b7dcd5a0e7 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -67,6 +67,9 @@ config AUTO_IRQ_AFFINITY depends on SMP default y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" @@ -612,6 +615,11 @@ config VERBOSE_MCHECK_ON Take the default (1) unless you want more control or more info. +config HZ + int + default 1200 if ALPHA_RAWHIDE + default 1024 + source "drivers/pci/Kconfig" source "drivers/eisa/Kconfig" diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 72f9a619a66..973c5c3705e 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -22,7 +22,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/utsname.h> #include <linux/time.h> #include <linux/timex.h> diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 92b61629fe3..96ed82fd9ee 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -19,7 +19,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/utsname.h> #include <linux/time.h> #include <linux/major.h> @@ -318,68 +317,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, } /* - * Fill in the user structure for an ECOFF core dump. - */ -void -dump_thread(struct pt_regs * pt, struct user * dump) -{ - /* switch stack follows right below pt_regs: */ - struct switch_stack * sw = ((struct switch_stack *) pt) - 1; - - dump->magic = CMAGIC; - dump->start_code = current->mm->start_code; - dump->start_data = current->mm->start_data; - dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = ((current->mm->end_code - dump->start_code) - >> PAGE_SHIFT); - dump->u_dsize = ((current->mm->brk + PAGE_SIZE-1 - dump->start_data) - >> PAGE_SHIFT); - dump->u_ssize = (current->mm->start_stack - dump->start_stack - + PAGE_SIZE-1) >> PAGE_SHIFT; - - /* - * We store the registers in an order/format that is - * compatible with DEC Unix/OSF/1 as this makes life easier - * for gdb. - */ - dump->regs[EF_V0] = pt->r0; - dump->regs[EF_T0] = pt->r1; - dump->regs[EF_T1] = pt->r2; - dump->regs[EF_T2] = pt->r3; - dump->regs[EF_T3] = pt->r4; - dump->regs[EF_T4] = pt->r5; - dump->regs[EF_T5] = pt->r6; - dump->regs[EF_T6] = pt->r7; - dump->regs[EF_T7] = pt->r8; - dump->regs[EF_S0] = sw->r9; - dump->regs[EF_S1] = sw->r10; - dump->regs[EF_S2] = sw->r11; - dump->regs[EF_S3] = sw->r12; - dump->regs[EF_S4] = sw->r13; - dump->regs[EF_S5] = sw->r14; - dump->regs[EF_S6] = sw->r15; - dump->regs[EF_A3] = pt->r19; - dump->regs[EF_A4] = pt->r20; - dump->regs[EF_A5] = pt->r21; - dump->regs[EF_T8] = pt->r22; - dump->regs[EF_T9] = pt->r23; - dump->regs[EF_T10] = pt->r24; - dump->regs[EF_T11] = pt->r25; - dump->regs[EF_RA] = pt->r26; - dump->regs[EF_T12] = pt->r27; - dump->regs[EF_AT] = pt->r28; - dump->regs[EF_SP] = rdusp(); - dump->regs[EF_PS] = pt->ps; - dump->regs[EF_PC] = pt->pc; - dump->regs[EF_GP] = pt->gp; - dump->regs[EF_A0] = pt->r16; - dump->regs[EF_A1] = pt->r17; - dump->regs[EF_A2] = pt->r18; - memcpy((char *)dump->regs + EF_SIZE, sw->fp, 32 * 8); -} -EXPORT_SYMBOL(dump_thread); - -/* * Fill in the user structure for a ELF core dump. */ void diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 74c34662565..a449e999027 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -18,7 +18,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/mc146818rtc.h> @@ -1472,7 +1471,7 @@ c_stop(struct seq_file *f, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e19e7744e36..4127af93c5f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -126,6 +126,9 @@ config GENERIC_CALIBRATE_DELAY bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + config ARCH_MAY_HAVE_PC_FDC bool diff --git a/arch/arm/configs/omap_h2_1610_defconfig b/arch/arm/configs/omap_h2_1610_defconfig index b8a78ab49cd..c2345af3707 100644 --- a/arch/arm/configs/omap_h2_1610_defconfig +++ b/arch/arm/configs/omap_h2_1610_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.23-rc6 -# Mon Sep 17 14:21:45 2007 +# Linux kernel version: 2.6.24-rc5 +# Mon Dec 17 20:04:38 2007 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -41,9 +41,14 @@ CONFIG_SYSVIPC_SYSCTL=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -64,7 +69,6 @@ CONFIG_FUTEX=y CONFIG_ANON_INODES=y CONFIG_EPOLL=y CONFIG_SIGNALFD=y -CONFIG_TIMERFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_VM_EVENT_COUNTERS=y @@ -224,10 +228,6 @@ CONFIG_ARM_THUMB=y # # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set - -# -# PCCARD (PCMCIA/CardBus) support -# # CONFIG_PCCARD is not set # @@ -236,6 +236,7 @@ CONFIG_ARM_THUMB=y CONFIG_TICK_ONESHOT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y CONFIG_PREEMPT=y CONFIG_HZ=128 CONFIG_AEABI=y @@ -248,6 +249,7 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 # CONFIG_RESOURCES_64BIT is not set CONFIG_ZONE_DMA_FLAG=1 @@ -275,6 +277,8 @@ CONFIG_CPU_FREQ_STAT=y # CONFIG_CPU_FREQ_STAT_DETAILS is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set CONFIG_CPU_FREQ_GOV_USERSPACE=y @@ -347,6 +351,7 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_INET_XFRM_MODE_TRANSPORT=y CONFIG_INET_XFRM_MODE_TUNNEL=y CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set @@ -372,10 +377,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_LAPB is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# # CONFIG_NET_SCHED is not set # @@ -404,6 +405,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # # Generic Driver Options # +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set @@ -422,6 +424,8 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 # CONFIG_CDROM_PKTCDVD is not set CONFIG_ATA_OVER_ETH=m +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set # # SCSI device support @@ -459,6 +463,7 @@ CONFIG_SCSI_WAIT_SCAN=m # CONFIG_SCSI_FC_ATTRS is not set # CONFIG_SCSI_ISCSI_ATTRS is not set # CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set CONFIG_SCSI_LOWLEVEL=y # CONFIG_ISCSI_TCP is not set # CONFIG_SCSI_DEBUG is not set @@ -471,12 +476,18 @@ CONFIG_NETDEVICES=y # CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set +# CONFIG_VETH is not set # CONFIG_PHYLIB is not set CONFIG_NET_ETHERNET=y CONFIG_MII=y # CONFIG_AX88796 is not set CONFIG_SMC91X=y # CONFIG_DM9000 is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set CONFIG_NETDEV_1000=y CONFIG_NETDEV_10000=y @@ -522,7 +533,6 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set CONFIG_INPUT_EVDEV=y CONFIG_INPUT_EVBUG=y @@ -576,20 +586,51 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set # CONFIG_IPMI_HANDLER is not set -CONFIG_WATCHDOG=y -CONFIG_WATCHDOG_NOWAYOUT=y - -# -# Watchdog Device Drivers -# -# CONFIG_SOFT_WATCHDOG is not set -# CONFIG_OMAP_WATCHDOG is not set # CONFIG_HW_RANDOM is not set # CONFIG_NVRAM is not set # CONFIG_R3964 is not set # CONFIG_RAW_DRIVER is not set # CONFIG_TCG_TPM is not set -# CONFIG_I2C is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_ISP1301_OMAP is not set +CONFIG_TPS65010=y +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set # # SPI support @@ -597,28 +638,73 @@ CONFIG_WATCHDOG_NOWAYOUT=y # CONFIG_SPI is not set # CONFIG_SPI_MASTER is not set # CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set CONFIG_HWMON=y # CONFIG_HWMON_VID is not set -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set # CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set # CONFIG_SENSORS_PC87360 is not set # CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set # CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set # CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_THMC50 is not set # CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set # CONFIG_SENSORS_W83627HF is not set # CONFIG_SENSORS_W83627EHF is not set # CONFIG_HWMON_DEBUG_CHIP is not set -CONFIG_MISC_DEVICES=y -# CONFIG_EEPROM_93CX6 is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_OMAP_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set # # Multifunction device drivers # # CONFIG_MFD_SM501 is not set -# CONFIG_NEW_LEDS is not set # # Multimedia devices @@ -630,12 +716,6 @@ CONFIG_DAB=y # # Graphics support # -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Display device support -# -# CONFIG_DISPLAY_SUPPORT is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=m CONFIG_FB=y @@ -644,6 +724,7 @@ CONFIG_FIRMWARE_EDID=y # CONFIG_FB_CFB_FILLRECT is not set # CONFIG_FB_CFB_COPYAREA is not set # CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set # CONFIG_FB_SYS_FILLRECT is not set # CONFIG_FB_SYS_COPYAREA is not set # CONFIG_FB_SYS_IMAGEBLIT is not set @@ -659,8 +740,14 @@ CONFIG_FB_MODE_HELPERS=y # Frame buffer hardware drivers # # CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_OMAP is not set # CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_OMAP is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set # # Console display driver support @@ -705,6 +792,7 @@ CONFIG_SOUND_PRIME=y CONFIG_HID_SUPPORT=y CONFIG_HID=y CONFIG_HID_DEBUG=y +# CONFIG_HIDRAW is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y @@ -720,23 +808,11 @@ CONFIG_USB_ARCH_HAS_OHCI=y # # CONFIG_USB_GADGET is not set # CONFIG_MMC is not set +# CONFIG_NEW_LEDS is not set CONFIG_RTC_LIB=y # CONFIG_RTC_CLASS is not set # -# DMA Engine support -# -# CONFIG_DMA_ENGINE is not set - -# -# DMA Clients -# - -# -# DMA Devices -# - -# # File systems # CONFIG_EXT2_FS=y @@ -771,8 +847,9 @@ CONFIG_DNOTIFY=y # CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set # @@ -783,7 +860,6 @@ CONFIG_PROC_SYSCTL=y CONFIG_SYSFS=y # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y # CONFIG_CONFIGFS_FS is not set # @@ -802,10 +878,7 @@ CONFIG_CRAMFS=y # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set - -# -# Network File Systems -# +CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y # CONFIG_NFS_V3_ACL is not set @@ -832,13 +905,9 @@ CONFIG_RPCSEC_GSS_KRB5=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -862,7 +931,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set # CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set @@ -876,21 +945,16 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set # CONFIG_NLS_UTF8 is not set - -# -# Distributed Lock Manager -# # CONFIG_DLM is not set - -# -# Profiling support -# +CONFIG_INSTRUMENTATION=y # CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set # # Kernel hacking # # CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y # CONFIG_MAGIC_SYSRQ is not set # CONFIG_UNUSED_SYMBOLS is not set @@ -899,6 +963,7 @@ CONFIG_ENABLE_MUST_CHECK=y # CONFIG_DEBUG_KERNEL is not set CONFIG_DEBUG_BUGVERBOSE=y CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set # CONFIG_DEBUG_USER is not set # @@ -906,6 +971,7 @@ CONFIG_FRAME_POINTER=y # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set CONFIG_CRYPTO=y CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_BLKCIPHER=y @@ -925,6 +991,7 @@ CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_PCBC=m # CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_XTS is not set # CONFIG_CRYPTO_CRYPTD is not set CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_FCRYPT is not set @@ -938,11 +1005,13 @@ CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_ARC4 is not set # CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_SEED is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set # CONFIG_CRYPTO_CAMELLIA is not set # CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_AUTHENC is not set CONFIG_CRYPTO_HW=y # diff --git a/arch/arm/configs/omap_osk_5912_defconfig b/arch/arm/configs/omap_osk_5912_defconfig index 8c1f15c7c45..d592a648711 100644 --- a/arch/arm/configs/omap_osk_5912_defconfig +++ b/arch/arm/configs/omap_osk_5912_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.23-rc6 -# Mon Sep 17 14:15:05 2007 +# Linux kernel version: 2.6.24-rc5 +# Mon Dec 17 21:12:45 2007 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -40,9 +40,14 @@ CONFIG_SYSVIPC_SYSCTL=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -63,7 +68,6 @@ CONFIG_FUTEX=y CONFIG_ANON_INODES=y CONFIG_EPOLL=y CONFIG_SIGNALFD=y -CONFIG_TIMERFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_VM_EVENT_COUNTERS=y @@ -224,10 +228,6 @@ CONFIG_CPU_CP15_MMU=y # # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set - -# -# PCCARD (PCMCIA/CardBus) support -# CONFIG_PCCARD=y # CONFIG_PCMCIA_DEBUG is not set CONFIG_PCMCIA=y @@ -245,6 +245,7 @@ CONFIG_OMAP_CF=y CONFIG_TICK_ONESHOT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y # CONFIG_PREEMPT is not set CONFIG_HZ=128 CONFIG_AEABI=y @@ -257,6 +258,7 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 # CONFIG_RESOURCES_64BIT is not set CONFIG_ZONE_DMA_FLAG=1 @@ -346,6 +348,7 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_INET_XFRM_MODE_TRANSPORT=y CONFIG_INET_XFRM_MODE_TUNNEL=y CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set @@ -371,10 +374,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_LAPB is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# # CONFIG_NET_SCHED is not set # @@ -403,6 +402,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # # Generic Driver Options # +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y @@ -427,6 +427,7 @@ CONFIG_MTD_BLOCK=y # CONFIG_INFTL is not set # CONFIG_RFD_FTL is not set # CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set # # RAM/ROM/Flash chip drivers @@ -495,6 +496,8 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set CONFIG_IDE=m CONFIG_BLK_DEV_IDE=m @@ -515,9 +518,10 @@ CONFIG_IDE_PROC_FS=y # IDE chipset support/bugfixes # # CONFIG_IDE_GENERIC is not set -# CONFIG_IDEPCI_PCIBUS_ORDER is not set +# CONFIG_BLK_DEV_PLATFORM is not set # CONFIG_IDE_ARM is not set # CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y # CONFIG_BLK_DEV_HD is not set # @@ -536,12 +540,18 @@ CONFIG_NETDEVICES=y # CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set +# CONFIG_VETH is not set # CONFIG_PHYLIB is not set CONFIG_NET_ETHERNET=y CONFIG_MII=y # CONFIG_AX88796 is not set CONFIG_SMC91X=y # CONFIG_DM9000 is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set CONFIG_NETDEV_1000=y CONFIG_NETDEV_10000=y @@ -585,7 +595,6 @@ CONFIG_INPUT_MOUSEDEV=y CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_EVBUG is not set @@ -651,7 +660,6 @@ CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_IPMI_HANDLER is not set -# CONFIG_WATCHDOG is not set CONFIG_HW_RANDOM=m CONFIG_HW_RANDOM_OMAP=m # CONFIG_NVRAM is not set @@ -712,10 +720,9 @@ CONFIG_TPS65010=y # CONFIG_SPI is not set # CONFIG_SPI_MASTER is not set # CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set CONFIG_HWMON=y # CONFIG_HWMON_VID is not set -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ABITUGURU3 is not set # CONFIG_SENSORS_AD7418 is not set # CONFIG_SENSORS_ADM1021 is not set # CONFIG_SENSORS_ADM1025 is not set @@ -723,12 +730,12 @@ CONFIG_HWMON=y # CONFIG_SENSORS_ADM1029 is not set # CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ADM9240 is not set -# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ADT7470 is not set # CONFIG_SENSORS_ATXP1 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_F71805F is not set -# CONFIG_SENSORS_FSCHER is not set -# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_GL520SM is not set # CONFIG_SENSORS_IT87 is not set @@ -761,14 +768,18 @@ CONFIG_HWMON=y # CONFIG_SENSORS_W83627HF is not set # CONFIG_SENSORS_W83627EHF is not set # CONFIG_HWMON_DEBUG_CHIP is not set -CONFIG_MISC_DEVICES=y -# CONFIG_EEPROM_93CX6 is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set # # Multifunction device drivers # # CONFIG_MFD_SM501 is not set -# CONFIG_NEW_LEDS is not set # # Multimedia devices @@ -780,12 +791,6 @@ CONFIG_DAB=y # # Graphics support # -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Display device support -# -# CONFIG_DISPLAY_SUPPORT is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=m CONFIG_FB=y @@ -794,6 +799,7 @@ CONFIG_FIRMWARE_EDID=y # CONFIG_FB_CFB_FILLRECT is not set # CONFIG_FB_CFB_COPYAREA is not set # CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set # CONFIG_FB_SYS_FILLRECT is not set # CONFIG_FB_SYS_COPYAREA is not set # CONFIG_FB_SYS_IMAGEBLIT is not set @@ -809,8 +815,14 @@ CONFIG_FB_MODE_HELPERS=y # Frame buffer hardware drivers # # CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_OMAP is not set # CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_OMAP is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set # # Console display driver support @@ -843,6 +855,7 @@ CONFIG_LOGO_LINUX_CLUT224=y CONFIG_HID_SUPPORT=y CONFIG_HID=y CONFIG_HID_DEBUG=y +# CONFIG_HIDRAW is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y @@ -858,23 +871,11 @@ CONFIG_USB_ARCH_HAS_OHCI=y # # CONFIG_USB_GADGET is not set # CONFIG_MMC is not set +# CONFIG_NEW_LEDS is not set CONFIG_RTC_LIB=y # CONFIG_RTC_CLASS is not set # -# DMA Engine support -# -# CONFIG_DMA_ENGINE is not set - -# -# DMA Clients -# - -# -# DMA Devices -# - -# # File systems # CONFIG_EXT2_FS=y @@ -922,7 +923,6 @@ CONFIG_PROC_SYSCTL=y CONFIG_SYSFS=y # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y # CONFIG_CONFIGFS_FS is not set # @@ -938,10 +938,12 @@ CONFIG_RAMFS=y CONFIG_JFFS2_FS=y CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set # CONFIG_JFFS2_SUMMARY is not set # CONFIG_JFFS2_FS_XATTR is not set # CONFIG_JFFS2_COMPRESSION_OPTIONS is not set CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set CONFIG_JFFS2_RTIME=y # CONFIG_JFFS2_RUBIN is not set # CONFIG_CRAMFS is not set @@ -950,10 +952,7 @@ CONFIG_JFFS2_RTIME=y # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set - -# -# Network File Systems -# +CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y # CONFIG_NFS_V3_ACL is not set @@ -979,10 +978,6 @@ CONFIG_SUNRPC=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# CONFIG_NLS=m CONFIG_NLS_DEFAULT="iso8859-1" CONFIG_NLS_CODEPAGE_437=m @@ -1023,21 +1018,16 @@ CONFIG_NLS_ISO8859_1=m # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set # CONFIG_NLS_UTF8 is not set - -# -# Distributed Lock Manager -# # CONFIG_DLM is not set - -# -# Profiling support -# +CONFIG_INSTRUMENTATION=y # CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set # # Kernel hacking # # CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y # CONFIG_MAGIC_SYSRQ is not set # CONFIG_UNUSED_SYMBOLS is not set @@ -1046,6 +1036,7 @@ CONFIG_ENABLE_MUST_CHECK=y # CONFIG_DEBUG_KERNEL is not set CONFIG_DEBUG_BUGVERBOSE=y CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set # CONFIG_DEBUG_USER is not set # @@ -1053,6 +1044,7 @@ CONFIG_FRAME_POINTER=y # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set # CONFIG_CRYPTO is not set # diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 4f1a03124a7..46bf2ede612 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -18,7 +18,6 @@ #include <linux/unistd.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/interrupt.h> @@ -368,35 +367,6 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) EXPORT_SYMBOL(dump_fpu); /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - struct task_struct *tsk = current; - - dump->magic = CMAGIC; - dump->start_code = tsk->mm->start_code; - dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1); - - dump->u_tsize = (tsk->mm->end_code - tsk->mm->start_code) >> PAGE_SHIFT; - dump->u_dsize = (tsk->mm->brk - tsk->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT; - dump->u_ssize = 0; - - dump->u_debugreg[0] = tsk->thread.debug.bp[0].address; - dump->u_debugreg[1] = tsk->thread.debug.bp[1].address; - dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn.arm; - dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm; - dump->u_debugreg[4] = tsk->thread.debug.nsaved; - - if (dump->start_stack < 0x04000000) - dump->u_ssize = (0x04000000 - dump->start_stack) >> PAGE_SHIFT; - - dump->regs = *regs; - dump->u_fpvalid = dump_fpu (regs, &dump->u_fp); -} -EXPORT_SYMBOL(dump_thread); - -/* * Shuffle the argument into the correct register before calling the * thread function. r1 is the thread argument, r2 is the pointer to * the thread function, and r3 points to the exit function. diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d3941a7b045..b7b0720bc1b 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -1001,7 +1001,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c index ec76eeaafd4..de6424e9ac0 100644 --- a/arch/arm/mach-at91/clock.c +++ b/arch/arm/mach-at91/clock.c @@ -343,7 +343,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) EXPORT_SYMBOL(clk_set_parent); /* establish PCK0..PCK3 parentage and rate */ -static void init_programmable_clock(struct clk *clk) +static void __init init_programmable_clock(struct clk *clk) { struct clk *parent; u32 pckr; diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 139ceaa35e2..4143828a968 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -290,7 +290,7 @@ static int davinci_ck_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations davinci_ck_op = { +static const struct seq_operations davinci_ck_op = { .start = davinci_ck_start, .next = davinci_ck_next, .stop = davinci_ck_stop, @@ -302,7 +302,7 @@ static int davinci_ck_open(struct inode *inode, struct file *file) return seq_open(file, &davinci_ck_op); } -static struct file_operations proc_davinci_ck_operations = { +static const struct file_operations proc_davinci_ck_operations = { .open = davinci_ck_open, .read = seq_read, .llseek = seq_lseek, diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 391b6f4827f..015a66b3ca8 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -13,20 +13,20 @@ obj-$(CONFIG_PM) += pm.o sleep.o led-y := leds.o # Specific board support -obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o +obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o board-h2-mmc.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 obj-$(CONFIG_MACH_OMAP_FSAMPLE) += board-fsample.o obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o -obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o +obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o board-h3-mmc.o obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.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 -obj-$(CONFIG_MACH_SX1) += board-sx1.o +obj-$(CONFIG_MACH_SX1) += board-sx1.o board-sx1-mmc.o ifeq ($(CONFIG_ARCH_OMAP15XX),y) # Innovator-1510 FPGA diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index c73ca61e585..8b102ad59c1 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -135,21 +135,21 @@ static void __init ams_delta_init_irq(void) } static struct map_desc ams_delta_io_desc[] __initdata = { - // AMS_DELTA_LATCH1 + /* AMS_DELTA_LATCH1 */ { .virtual = AMS_DELTA_LATCH1_VIRT, .pfn = __phys_to_pfn(AMS_DELTA_LATCH1_PHYS), .length = 0x01000000, .type = MT_DEVICE }, - // AMS_DELTA_LATCH2 + /* AMS_DELTA_LATCH2 */ { .virtual = AMS_DELTA_LATCH2_VIRT, .pfn = __phys_to_pfn(AMS_DELTA_LATCH2_PHYS), .length = 0x01000000, .type = MT_DEVICE }, - // AMS_DELTA_MODEM + /* AMS_DELTA_MODEM */ { .virtual = AMS_DELTA_MODEM_VIRT, .pfn = __phys_to_pfn(AMS_DELTA_MODEM_PHYS), @@ -227,6 +227,7 @@ static void __init ams_delta_init(void) omap_board_config = ams_delta_config; omap_board_config_size = ARRAY_SIZE(ams_delta_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); /* Clear latch2 (NAND, LCD, modem enable) */ ams_delta_latch2_write(~0, 0); diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index f550b19e1ec..1bdb66638e2 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -30,6 +30,7 @@ #include <asm/arch/gpio.h> #include <asm/arch/mux.h> #include <asm/arch/fpga.h> +#include <asm/arch/nand.h> #include <asm/arch/keypad.h> #include <asm/arch/common.h> #include <asm/arch/board.h> @@ -134,7 +135,7 @@ static struct platform_device nor_device = { .resource = &nor_resource, }; -static struct nand_platform_data nand_data = { +static struct omap_nand_platform_data nand_data = { .options = NAND_SAMSUNG_LP_OPTIONS, }; @@ -202,7 +203,7 @@ static struct platform_device *devices[] __initdata = { #define P2_NAND_RB_GPIO_PIN 62 -static int nand_dev_ready(struct nand_platform_data *data) +static int nand_dev_ready(struct omap_nand_platform_data *data) { return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN); } @@ -233,6 +234,7 @@ static void __init omap_fsample_init(void) omap_board_config = fsample_config; omap_board_config_size = ARRAY_SIZE(fsample_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init fsample_init_smc91x(void) diff --git a/arch/arm/mach-omap1/board-generic.c b/arch/arm/mach-omap1/board-generic.c index 33d01adab1e..c711bf23f7b 100644 --- a/arch/arm/mach-omap1/board-generic.c +++ b/arch/arm/mach-omap1/board-generic.c @@ -55,33 +55,14 @@ static struct omap_usb_config generic1610_usb_config __initdata = { .hmc_mode = 16, .pins[0] = 6, }; - -static struct omap_mmc_config generic_mmc_config __initdata = { - .mmc [0] = { - .enabled = 0, - .wire4 = 0, - .wp_pin = -1, - .power_pin = -1, - .switch_pin = -1, - }, - .mmc [1] = { - .enabled = 0, - .wire4 = 0, - .wp_pin = -1, - .power_pin = -1, - .switch_pin = -1, - }, -}; - #endif static struct omap_uart_config generic_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; -static struct omap_board_config_kernel generic_config[] = { - { OMAP_TAG_USB, NULL }, - { OMAP_TAG_MMC, &generic_mmc_config }, +static struct omap_board_config_kernel generic_config[] __initdata = { + { OMAP_TAG_USB, NULL }, { OMAP_TAG_UART, &generic_uart_config }, }; @@ -101,6 +82,7 @@ static void __init omap_generic_init(void) omap_board_config = generic_config; omap_board_config_size = ARRAY_SIZE(generic_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init omap_generic_map_io(void) 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 00000000000..6fdc78406b2 --- /dev/null +++ b/arch/arm/mach-omap1/board-h2-mmc.c @@ -0,0 +1,110 @@ +/* + * 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 + * + * 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. + */ + +#include <asm/arch/mmc.h> +#include <asm/arch/gpio.h> + +#ifdef CONFIG_MMC_OMAP +static int slot_cover_open; +static struct device *mmc_device; + +static int h2_mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1, + power_on ? "on" : "off", vdd); +#endif + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + return 0; +} + +static int h2_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode) +{ +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1, + bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull"); +#endif + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + return 0; +} + +static int h2_mmc_get_cover_state(struct device *dev, int slot) +{ + BUG_ON(slot != 0); + + return slot_cover_open; +} + +void h2_mmc_slot_cover_handler(void *arg, int state) +{ + if (mmc_device == NULL) + return; + + slot_cover_open = state; + omap_mmc_notify_cover_event(mmc_device, 0, state); +} + +static int h2_mmc_late_init(struct device *dev) +{ + int ret = 0; + + mmc_device = dev; + + return ret; +} + +static void h2_mmc_cleanup(struct device *dev) +{ +} + +static struct omap_mmc_platform_data h2_mmc_data = { + .nr_slots = 1, + .switch_slot = NULL, + .init = h2_mmc_late_init, + .cleanup = h2_mmc_cleanup, + .slots[0] = { + .set_power = h2_mmc_set_power, + .set_bus_mode = h2_mmc_set_bus_mode, + .get_ro = NULL, + .get_cover_state = h2_mmc_get_cover_state, + .ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 | + MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +void __init h2_mmc_init(void) +{ + omap_set_mmc_info(1, &h2_mmc_data); +} + +#else + +void __init h2_mmc_init(void) +{ +} + +void h2_mmc_slot_cover_handler(void *arg, int state) +{ +} +#endif diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index bfa04fa2552..070345ee39a 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -37,8 +37,10 @@ #include <asm/mach/flash.h> #include <asm/mach/map.h> +#include <asm/arch/gpio-switch.h> #include <asm/arch/mux.h> #include <asm/arch/tc.h> +#include <asm/arch/nand.h> #include <asm/arch/irda.h> #include <asm/arch/usb.h> #include <asm/arch/keypad.h> @@ -46,8 +48,6 @@ #include <asm/arch/mcbsp.h> #include <asm/arch/omap-alsa.h> -extern int omap_gpio_init(void); - static int h2_keymap[] = { KEY(0, 0, KEY_LEFT), KEY(0, 1, KEY_RIGHT), @@ -140,8 +140,6 @@ static struct platform_device h2_nor_device = { .resource = &h2_nor_resource, }; -#if 0 /* REVISIT: Enable when nand_platform_data is applied */ - static struct mtd_partition h2_nand_partitions[] = { #if 0 /* REVISIT: enable these partitions if you make NAND BOOT @@ -179,7 +177,7 @@ static struct mtd_partition h2_nand_partitions[] = { }; /* dip switches control NAND chip access: 8 bit, 16 bit, or neither */ -static struct nand_platform_data h2_nand_data = { +static struct omap_nand_platform_data h2_nand_data = { .options = NAND_SAMSUNG_LP_OPTIONS, .parts = h2_nand_partitions, .nr_parts = ARRAY_SIZE(h2_nand_partitions), @@ -198,7 +196,6 @@ static struct platform_device h2_nand_device = { .num_resources = 1, .resource = &h2_nand_resource, }; -#endif static struct resource h2_smc91x_resources[] = { [0] = { @@ -311,18 +308,18 @@ static struct omap_mcbsp_reg_cfg mcbsp_regs = { .srgr2 = GSYNC | CLKSP | FSGM | FPER(31), .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP, - //.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */ + /*.pcr0 = CLKXP | CLKRP,*/ /* mcbsp: slave */ }; static struct omap_alsa_codec_config alsa_config = { .name = "H2 TSC2101", .mcbsp_regs_alsa = &mcbsp_regs, - .codec_configure_dev = NULL, // tsc2101_configure, - .codec_set_samplerate = NULL, // tsc2101_set_samplerate, - .codec_clock_setup = NULL, // tsc2101_clock_setup, - .codec_clock_on = NULL, // tsc2101_clock_on, - .codec_clock_off = NULL, // tsc2101_clock_off, - .get_default_samplerate = NULL, // tsc2101_get_default_samplerate, + .codec_configure_dev = NULL, /* tsc2101_configure, */ + .codec_set_samplerate = NULL, /* tsc2101_set_samplerate, */ + .codec_clock_setup = NULL, /* tsc2101_clock_setup, */ + .codec_clock_on = NULL, /* tsc2101_clock_on, */ + .codec_clock_off = NULL, /* tsc2101_clock_off, */ + .get_default_samplerate = NULL, /* tsc2101_get_default_samplerate, */ }; static struct platform_device h2_mcbsp1_device = { @@ -335,7 +332,7 @@ static struct platform_device h2_mcbsp1_device = { static struct platform_device *h2_devices[] __initdata = { &h2_nor_device, - //&h2_nand_device, + &h2_nand_device, &h2_smc91x_device, &h2_irda_device, &h2_kp_device, @@ -343,22 +340,6 @@ static struct platform_device *h2_devices[] __initdata = { &h2_mcbsp1_device, }; -#ifdef CONFIG_I2C_BOARDINFO -static struct i2c_board_info __initdata h2_i2c_board_info[] = { - { - I2C_BOARD_INFO("tps65010", 0x48), - .type = "tps65010", - .irq = OMAP_GPIO_IRQ(58), - }, - /* TODO when driver support is ready: - * - isp1301 OTG transceiver - * - optional ov9640 camera sensor at 0x30 - * - pcf9754 for aGPS control - * - ... etc - */ -}; -#endif - static void __init h2_init_smc91x(void) { if ((omap_request_gpio(0)) < 0) { @@ -367,6 +348,14 @@ static void __init h2_init_smc91x(void) } } +static struct i2c_board_info __initdata h2_i2c_board_info[] = { + { + I2C_BOARD_INFO("isp1301_omap", 0x2d), + .type = "isp1301_omap", + .irq = OMAP_GPIO_IRQ(2), + }, +}; + static void __init h2_init_irq(void) { omap1_init_common_hw(); @@ -380,26 +369,25 @@ static struct omap_usb_config h2_usb_config __initdata = { .otg = 2, #ifdef CONFIG_USB_GADGET_OMAP - .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled - // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ + /* .hmc_mode = 21,*/ /* 0:host(off) 1:dev(loopback) 2:host(loopback) */ #elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) /* needs OTG cable, or NONSTANDARD (B-to-MiniB) */ - .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ #endif .pins[1] = 3, }; static struct omap_mmc_config h2_mmc_config __initdata = { - .mmc [0] = { - .enabled = 1, + .mmc[0] = { + .enabled = 1, .wire4 = 1, - .wp_pin = OMAP_MPUIO(3), - .power_pin = -1, /* tps65010 gpio3 */ - .switch_pin = OMAP_MPUIO(1), }, }; +extern struct omap_mmc_platform_data h2_mmc_data; + static struct omap_uart_config h2_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; @@ -409,15 +397,15 @@ static struct omap_lcd_config h2_lcd_config __initdata = { }; static struct omap_board_config_kernel h2_config[] __initdata = { - { OMAP_TAG_USB, &h2_usb_config }, - { OMAP_TAG_MMC, &h2_mmc_config }, + { OMAP_TAG_USB, &h2_usb_config }, + { OMAP_TAG_MMC, &h2_mmc_config }, { OMAP_TAG_UART, &h2_uart_config }, { OMAP_TAG_LCD, &h2_lcd_config }, }; #define H2_NAND_RB_GPIO_PIN 62 -static int h2_nand_dev_ready(struct nand_platform_data *data) +static int h2_nand_dev_ready(struct omap_nand_platform_data *data) { return omap_get_gpio_datain(H2_NAND_RB_GPIO_PIN); } @@ -436,18 +424,16 @@ static void __init h2_init(void) h2_nor_resource.end = h2_nor_resource.start = omap_cs3_phys(); h2_nor_resource.end += SZ_32M - 1; -#if 0 /* REVISIT: Enable when nand_platform_data is applied */ h2_nand_resource.end = h2_nand_resource.start = OMAP_CS2B_PHYS; h2_nand_resource.end += SZ_4K - 1; if (!(omap_request_gpio(H2_NAND_RB_GPIO_PIN))) h2_nand_data.dev_ready = h2_nand_dev_ready; -#endif 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(U19_ARMIO1); */ /* CD */ omap_cfg_reg(BALLOUT_V8_ARMIO3); /* WP */ /* Irda */ @@ -463,16 +449,9 @@ static void __init h2_init(void) omap_board_config = h2_config; omap_board_config_size = ARRAY_SIZE(h2_config); omap_serial_init(); - - /* irq for tps65010 chip */ - omap_cfg_reg(W4_GPIO58); - if (gpio_request(58, "tps65010") == 0) - gpio_direction_input(58); - -#ifdef CONFIG_I2C_BOARDINFO - i2c_register_board_info(1, h2_i2c_board_info, - ARRAY_SIZE(h2_i2c_board_info)); -#endif + omap_register_i2c_bus(1, 100, h2_i2c_board_info, + ARRAY_SIZE(h2_i2c_board_info)); + h2_mmc_init(); } static void __init h2_map_io(void) @@ -480,22 +459,6 @@ static void __init h2_map_io(void) omap1_map_common_io(); } -#ifdef CONFIG_TPS65010 -static int __init h2_tps_init(void) -{ - if (!machine_is_omap_h2()) - return 0; - - /* gpio3 for SD, gpio4 for VDD_DSP */ - /* FIXME send power to DSP iff it's configured */ - - /* Enable LOW_PWR */ - tps65010_set_low_pwr(ON); - return 0; -} -fs_initcall(h2_tps_init); -#endif - MACHINE_START(OMAP_H2, "TI-H2") /* Maintainer: Imre Deak <imre.deak@nokia.com> */ .phys_io = 0xfff00000, 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 00000000000..66ecc437928 --- /dev/null +++ b/arch/arm/mach-omap1/board-h3-mmc.c @@ -0,0 +1,114 @@ +/* + * 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 + * + * 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. + */ + +#include <asm/arch/mmc.h> +#include <asm/arch/gpio.h> + +#ifdef CONFIG_MMC_OMAP +static int slot_cover_open; +static struct device *mmc_device; + +static int h3_mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1, + power_on ? "on" : "off", vdd); +#endif + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + return 0; +} + +static int h3_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode) +{ + int ret = 0; + +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1, + bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull"); +#endif + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + /* Treated on upper level */ + + return bus_mode; +} + +static int h3_mmc_get_cover_state(struct device *dev, int slot) +{ + BUG_ON(slot != 0); + + return slot_cover_open; +} + +void h3_mmc_slot_cover_handler(void *arg, int state) +{ + if (mmc_device == NULL) + return; + + slot_cover_open = state; + omap_mmc_notify_cover_event(mmc_device, 0, state); +} + +static int h3_mmc_late_init(struct device *dev) +{ + int ret = 0; + + mmc_device = dev; + + return ret; +} + +static void h3_mmc_cleanup(struct device *dev) +{ +} + +static struct omap_mmc_platform_data h3_mmc_data = { + .nr_slots = 1, + .switch_slot = NULL, + .init = h3_mmc_late_init, + .cleanup = h3_mmc_cleanup, + .slots[0] = { + .set_power = h3_mmc_set_power, + .set_bus_mode = h3_mmc_set_bus_mode, + .get_ro = NULL, + .get_cover_state = h3_mmc_get_cover_state, + .ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 | + MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +void __init h3_mmc_init(void) +{ + omap_set_mmc_info(1, &h3_mmc_data); +} + +#else + +void __init h3_mmc_init(void) +{ +} + +void h3_mmc_slot_cover_handler(void *arg, int state) +{ +} +#endif diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 05651986056..6fc516855a8 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -42,6 +42,7 @@ #include <asm/arch/irqs.h> #include <asm/arch/mux.h> #include <asm/arch/tc.h> +#include <asm/arch/nand.h> #include <asm/arch/irda.h> #include <asm/arch/usb.h> #include <asm/arch/keypad.h> @@ -50,8 +51,6 @@ #include <asm/arch/mcbsp.h> #include <asm/arch/omap-alsa.h> -extern int omap_gpio_init(void); - static int h3_keymap[] = { KEY(0, 0, KEY_LEFT), KEY(0, 1, KEY_RIGHT), @@ -179,7 +178,7 @@ static struct mtd_partition nand_partitions[] = { }; /* dip switches control NAND chip access: 8 bit, 16 bit, or neither */ -static struct nand_platform_data nand_data = { +static struct omap_nand_platform_data nand_data = { .options = NAND_SAMSUNG_LP_OPTIONS, .parts = nand_partitions, .nr_parts = ARRAY_SIZE(nand_partitions), @@ -387,18 +386,18 @@ static struct omap_mcbsp_reg_cfg mcbsp_regs = { .srgr2 = GSYNC | CLKSP | FSGM | FPER(31), .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP, - //.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */ + /*.pcr0 = CLKXP | CLKRP,*/ /* mcbsp: slave */ }; static struct omap_alsa_codec_config alsa_config = { .name = "H3 TSC2101", .mcbsp_regs_alsa = &mcbsp_regs, - .codec_configure_dev = NULL, // tsc2101_configure, - .codec_set_samplerate = NULL, // tsc2101_set_samplerate, - .codec_clock_setup = NULL, // tsc2101_clock_setup, - .codec_clock_on = NULL, // tsc2101_clock_on, - .codec_clock_off = NULL, // tsc2101_clock_off, - .get_default_samplerate = NULL, // tsc2101_get_default_samplerate, + .codec_configure_dev = NULL, /* tsc2101_configure, */ + .codec_set_samplerate = NULL, /* tsc2101_set_samplerate, */ + .codec_clock_setup = NULL, /* tsc2101_clock_setup, */ + .codec_clock_on = NULL, /* tsc2101_clock_on, */ + .codec_clock_off = NULL, /* tsc2101_clock_off, */ + .get_default_samplerate = NULL, /* tsc2101_get_default_samplerate, */ }; static struct platform_device h3_mcbsp1_device = { @@ -436,12 +435,13 @@ static struct omap_usb_config h3_usb_config __initdata = { static struct omap_mmc_config h3_mmc_config __initdata = { .mmc[0] = { - .enabled = 1, - .power_pin = -1, /* tps65010 GPIO4 */ - .switch_pin = OMAP_MPUIO(1), - }, + .enabled = 1, + .wire4 = 1, + }, }; +extern struct omap_mmc_platform_data h3_mmc_data; + static struct omap_uart_config h3_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; @@ -450,29 +450,28 @@ static struct omap_lcd_config h3_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel h3_config[] = { +static struct omap_board_config_kernel h3_config[] __initdata = { { OMAP_TAG_USB, &h3_usb_config }, { OMAP_TAG_MMC, &h3_mmc_config }, { OMAP_TAG_UART, &h3_uart_config }, { OMAP_TAG_LCD, &h3_lcd_config }, }; -static struct i2c_board_info __initdata h3_i2c_board_info[] = { +static struct omap_gpio_switch h3_gpio_switches[] __initdata = { { - I2C_BOARD_INFO("tps65010", 0x48), - .type = "tps65013", - /* .irq = OMAP_GPIO_IRQ(??), */ + .name = "mmc_slot", + .gpio = OMAP_MPUIO(1), + .type = OMAP_GPIO_SWITCH_TYPE_COVER, + .debounce_rising = 100, + .debounce_falling = 0, + .notify = h3_mmc_slot_cover_handler, + .notify_data = NULL, }, - /* TODO when driver support is ready: - * - isp1301 OTG transceiver - * - optional ov9640 camera sensor at 0x30 - * - ... - */ }; #define H3_NAND_RB_GPIO_PIN 10 -static int nand_dev_ready(struct nand_platform_data *data) +static int nand_dev_ready(struct omap_nand_platform_data *data) { return omap_get_gpio_datain(H3_NAND_RB_GPIO_PIN); } @@ -500,13 +499,14 @@ static void __init h3_init(void) omap_cfg_reg(V2_1710_GPIO10); platform_add_devices(devices, ARRAY_SIZE(devices)); + spi_register_board_info(h3_spi_board_info, + ARRAY_SIZE(h3_spi_board_info)); omap_board_config = h3_config; omap_board_config_size = ARRAY_SIZE(h3_config); omap_serial_init(); - - /* FIXME setup irq for tps65013 chip */ - i2c_register_board_info(1, h3_i2c_board_info, - ARRAY_SIZE(h3_i2c_board_info)); + omap_register_i2c_bus(1, 100, h3_i2c_board_info, + ARRAY_SIZE(h3_i2c_board_info)); + h3_mmc_init(); } static void __init h3_init_smc91x(void) @@ -531,23 +531,6 @@ static void __init h3_map_io(void) omap1_map_common_io(); } -#ifdef CONFIG_TPS65010 -static int __init h3_tps_init(void) -{ - if (!machine_is_omap_h3()) - return 0; - - /* gpio4 for SD, gpio3 for VDD_DSP */ - /* FIXME send power to DSP iff it's configured */ - - /* Enable LOW_PWR */ - tps65013_set_low_pwr(ON); - - return 0; -} -fs_initcall(h3_tps_init); -#endif - MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") /* Maintainer: Texas Instruments, Inc. */ .phys_io = 0xfff00000, diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index 7d2d8af155a..4b8ae3ee0d0 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -134,12 +134,12 @@ static struct omap_mcbsp_reg_cfg mcbsp_regs = { static struct omap_alsa_codec_config alsa_config = { .name = "OMAP Innovator AIC23", .mcbsp_regs_alsa = &mcbsp_regs, - .codec_configure_dev = NULL, // aic23_configure, - .codec_set_samplerate = NULL, // aic23_set_samplerate, - .codec_clock_setup = NULL, // aic23_clock_setup, - .codec_clock_on = NULL, // aic23_clock_on, - .codec_clock_off = NULL, // aic23_clock_off, - .get_default_samplerate = NULL, // aic23_get_default_samplerate, + .codec_configure_dev = NULL, /* aic23_configure, */ + .codec_set_samplerate = NULL, /* aic23_set_samplerate, */ + .codec_clock_setup = NULL, /* aic23_clock_setup, */ + .codec_clock_on = NULL, /* aic23_clock_on, */ + .codec_clock_off = NULL, /* aic23_clock_off, */ + .get_default_samplerate = NULL, /* aic23_get_default_samplerate, */ }; static struct platform_device innovator_mcbsp1_device = { @@ -345,11 +345,11 @@ static struct omap_usb_config h2_usb_config __initdata = { .otg = 2, #ifdef CONFIG_USB_GADGET_OMAP - .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled - // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ + /* .hmc_mode = 21,*/ /* 0:host(off) 1:dev(loopback) 2:host(loopback) */ #elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ - .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ #endif .pins[1] = 3, @@ -411,6 +411,7 @@ static void __init innovator_init(void) omap_board_config = innovator_config; omap_board_config_size = ARRAY_SIZE(innovator_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init innovator_map_io(void) diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c index e2c8ffd75cf..bcb984f2300 100644 --- a/arch/arm/mach-omap1/board-nokia770.c +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -189,7 +189,7 @@ static struct omap_mmc_config nokia770_mmc_config __initdata = { }, }; -static struct omap_board_config_kernel nokia770_config[] = { +static struct omap_board_config_kernel nokia770_config[] __initdata = { { OMAP_TAG_USB, NULL }, { OMAP_TAG_MMC, &nokia770_mmc_config }, }; @@ -330,6 +330,7 @@ static void __init omap_nokia770_init(void) omap_board_config_size = ARRAY_SIZE(nokia770_config); omap_gpio_init(); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); omap_dsp_init(); ads7846_dev_init(); mipid_dev_init(); diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 84333440008..5279e35a8ae 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -160,12 +160,12 @@ static struct omap_mcbsp_reg_cfg mcbsp_regs = { static struct omap_alsa_codec_config alsa_config = { .name = "OSK AIC23", .mcbsp_regs_alsa = &mcbsp_regs, - .codec_configure_dev = NULL, // aic23_configure, - .codec_set_samplerate = NULL, // aic23_set_samplerate, - .codec_clock_setup = NULL, // aic23_clock_setup, - .codec_clock_on = NULL, // aic23_clock_on, - .codec_clock_off = NULL, // aic23_clock_off, - .get_default_samplerate = NULL, // aic23_get_default_samplerate, + .codec_configure_dev = NULL, /* aic23_configure, */ + .codec_set_samplerate = NULL, /* aic23_set_samplerate, */ + .codec_clock_setup = NULL, /* aic23_clock_setup, */ + .codec_clock_on = NULL, /* aic23_clock_on, */ + .codec_clock_off = NULL, /* aic23_clock_off, */ + .get_default_samplerate = NULL, /* aic23_get_default_samplerate, */ }; static struct platform_device osk5912_mcbsp1_device = { @@ -253,7 +253,7 @@ static struct omap_lcd_config osk_lcd_config __initdata = { }; #endif -static struct omap_board_config_kernel osk_config[] = { +static struct omap_board_config_kernel osk_config[] __initdata = { { OMAP_TAG_USB, &osk_usb_config }, { OMAP_TAG_UART, &osk_uart_config }, #ifdef CONFIG_OMAP_OSK_MISTRAL @@ -392,7 +392,7 @@ static void __init osk_mistral_init(void) 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(Y14_1610_CCP_DATAM); */ omap_cfg_reg(W14_1610_CCP_DATAP); /* CAM_PWDN */ @@ -404,8 +404,8 @@ static void __init osk_mistral_init(void) pr_debug("OSK+Mistral: CAM_PWDN is awol\n"); - // omap_cfg_reg(P19_1610_GPIO6); // BUSY - omap_cfg_reg(P20_1610_GPIO4); // PENIRQ + /* omap_cfg_reg(P19_1610_GPIO6); */ /* BUSY */ + omap_cfg_reg(P20_1610_GPIO4); /* PENIRQ */ set_irq_type(OMAP_GPIO_IRQ(4), IRQT_FALLING); spi_register_board_info(mistral_boardinfo, ARRAY_SIZE(mistral_boardinfo)); @@ -473,10 +473,9 @@ static void __init osk_init(void) if (gpio_request(OMAP_MPUIO(1), "tps65010") == 0) gpio_direction_input(OMAP_MPUIO(1)); - i2c_register_board_info(1, osk_i2c_board_info, - ARRAY_SIZE(osk_i2c_board_info)); - omap_serial_init(); + omap_register_i2c_bus(1, 400, osk_i2c_board_info, + ARRAY_SIZE(osk_i2c_board_info)); osk_mistral_init(); } diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index 2f9d00a0013..ca1a4bf78a1 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -10,6 +10,8 @@ * Maintainers : http://palmtelinux.sf.net * palmtelinux-developpers@lists.sf.net * + * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org> + * * 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. @@ -24,8 +26,8 @@ #include <linux/spi/spi.h> #include <linux/spi/tsc2102.h> #include <linux/interrupt.h> +#include <linux/apm-emulation.h> -#include <asm/apm.h> #include <asm/hardware.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -51,11 +53,11 @@ static void __init omap_palmte_init_irq(void) omap_gpio_init(); } -static int palmte_keymap[] = { - KEY(0, 0, KEY_F1), - KEY(0, 1, KEY_F2), - KEY(0, 2, KEY_F3), - KEY(0, 3, KEY_F4), +static const int palmte_keymap[] = { + KEY(0, 0, KEY_F1), /* Calendar */ + KEY(0, 1, KEY_F2), /* Contacts */ + KEY(0, 2, KEY_F3), /* Tasks List */ + KEY(0, 3, KEY_F4), /* Note Pad */ KEY(0, 4, KEY_POWER), KEY(1, 0, KEY_LEFT), KEY(1, 1, KEY_DOWN), @@ -68,7 +70,7 @@ static int palmte_keymap[] = { static struct omap_kp_platform_data palmte_kp_data = { .rows = 8, .cols = 8, - .keymap = palmte_keymap, + .keymap = (int *) palmte_keymap, .rep = 1, .delay = 12, }; @@ -180,7 +182,7 @@ static struct platform_device palmte_irda_device = { .resource = palmte_irda_resources, }; -static struct platform_device *devices[] __initdata = { +static struct platform_device *palmte_devices[] __initdata = { &palmte_rom_device, &palmte_kp_device, &palmte_lcd_device, @@ -273,7 +275,7 @@ static void palmte_get_power_status(struct apm_power_info *info, int *battery) info->time = 0; } else { while (hi > lo + 1) { - mid = (hi + lo) >> 2; + mid = (hi + lo) >> 1; if (batt <= palmte_battery_sample[mid]) lo = mid; else @@ -321,7 +323,7 @@ static struct tsc2102_config palmte_tsc2102_config = { .alsa_config = &palmte_alsa_config, }; -static struct omap_board_config_kernel palmte_config[] = { +static struct omap_board_config_kernel palmte_config[] __initdata = { { OMAP_TAG_USB, &palmte_usb_config }, { OMAP_TAG_MMC, &palmte_mmc_config }, { OMAP_TAG_LCD, &palmte_lcd_config }, @@ -339,74 +341,34 @@ static struct spi_board_info palmte_spi_info[] __initdata = { }, }; -/* Periodically check for changes on important input pins */ -struct timer_list palmte_pin_timer; -int prev_power, prev_headphones; - -static void palmte_pin_handler(unsigned long data) { - int power, headphones; - - power = !omap_get_gpio_datain(PALMTE_DC_GPIO); - headphones = omap_get_gpio_datain(PALMTE_HEADPHONES_GPIO); - - if (power && !prev_power) - printk(KERN_INFO "PM: cable connected\n"); - else if (!power && prev_power) - printk(KERN_INFO "PM: cable disconnected\n"); - - if (headphones && !prev_headphones) { +static void palmte_headphones_detect(void *data, int state) +{ + if (state) { /* Headphones connected, disable speaker */ omap_set_gpio_dataout(PALMTE_SPEAKER_GPIO, 0); printk(KERN_INFO "PM: speaker off\n"); - } else if (!headphones && prev_headphones) { + } else { /* Headphones unplugged, re-enable speaker */ omap_set_gpio_dataout(PALMTE_SPEAKER_GPIO, 1); printk(KERN_INFO "PM: speaker on\n"); } - - prev_power = power; - prev_headphones = headphones; - mod_timer(&palmte_pin_timer, jiffies + msecs_to_jiffies(500)); } -static void __init palmte_gpio_setup(void) +static void __init palmte_misc_gpio_setup(void) { - /* Set TSC2102 PINTDAV pin as input */ + /* Set TSC2102 PINTDAV pin as input (used by TSC2102 driver) */ if (omap_request_gpio(PALMTE_PINTDAV_GPIO)) { printk(KERN_ERR "Could not reserve PINTDAV GPIO!\n"); return; } omap_set_gpio_direction(PALMTE_PINTDAV_GPIO, 1); - /* Monitor cable-connected signals */ - if (omap_request_gpio(PALMTE_DC_GPIO) || - omap_request_gpio(PALMTE_USB_OR_DC_GPIO) || - omap_request_gpio(PALMTE_USBDETECT_GPIO)) { + /* Set USB-or-DC-IN pin as input (unused) */ + if (omap_request_gpio(PALMTE_USB_OR_DC_GPIO)) { printk(KERN_ERR "Could not reserve cable signal GPIO!\n"); return; } - omap_set_gpio_direction(PALMTE_DC_GPIO, 1); omap_set_gpio_direction(PALMTE_USB_OR_DC_GPIO, 1); - omap_set_gpio_direction(PALMTE_USBDETECT_GPIO, 1); - - /* Set speaker-enable pin as output */ - if (omap_request_gpio(PALMTE_SPEAKER_GPIO)) { - printk(KERN_ERR "Could not reserve speaker GPIO!\n"); - return; - } - omap_set_gpio_direction(PALMTE_SPEAKER_GPIO, 0); - - /* Monitor the headphones-connected signal */ - if (omap_request_gpio(PALMTE_HEADPHONES_GPIO)) { - printk(KERN_ERR "Could not reserve headphones signal GPIO!\n"); - return; - } - omap_set_gpio_direction(PALMTE_HEADPHONES_GPIO, 1); - - prev_power = omap_get_gpio_datain(PALMTE_DC_GPIO); - prev_headphones = !omap_get_gpio_datain(PALMTE_HEADPHONES_GPIO); - setup_timer(&palmte_pin_timer, palmte_pin_handler, 0); - palmte_pin_handler(0); } static void __init omap_palmte_init(void) @@ -414,12 +376,12 @@ static void __init omap_palmte_init(void) omap_board_config = palmte_config; omap_board_config_size = ARRAY_SIZE(palmte_config); - platform_add_devices(devices, ARRAY_SIZE(devices)); + platform_add_devices(palmte_devices, ARRAY_SIZE(palmte_devices)); spi_register_board_info(palmte_spi_info, ARRAY_SIZE(palmte_spi_info)); - + palmte_misc_gpio_setup(); omap_serial_init(); - palmte_gpio_setup(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init omap_palmte_map_io(void) diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c index ed7094a7006..2a033689f9f 100644 --- a/arch/arm/mach-omap1/board-palmtt.c +++ b/arch/arm/mach-omap1/board-palmtt.c @@ -144,12 +144,12 @@ static struct omap_mcbsp_reg_cfg mcbsp_regs = { static struct omap_alsa_codec_config alsa_config = { .name = "PalmTT AIC23", .mcbsp_regs_alsa = &mcbsp_regs, - .codec_configure_dev = NULL, // aic23_configure, - .codec_set_samplerate = NULL, // aic23_set_samplerate, - .codec_clock_setup = NULL, // aic23_clock_setup, - .codec_clock_on = NULL, // aic23_clock_on, - .codec_clock_off = NULL, // aic23_clock_off, - .get_default_samplerate = NULL, // aic23_get_default_samplerate, + .codec_configure_dev = NULL, /* aic23_configure, */ + .codec_set_samplerate = NULL, /* aic23_set_samplerate, */ + .codec_clock_setup = NULL, /* aic23_clock_setup, */ + .codec_clock_on = NULL, /* aic23_clock_on, */ + .codec_clock_off = NULL, /* aic23_clock_off, */ + .get_default_samplerate = NULL, /* aic23_get_default_samplerate, */ }; static struct platform_device palmtt_mcbsp1_device = { @@ -312,7 +312,7 @@ static struct omap_uart_config palmtt_uart_config __initdata = { .enabled_uarts = (1 << 0) | (1 << 1) | (0 << 2), }; -static struct omap_board_config_kernel palmtt_config[] = { +static struct omap_board_config_kernel palmtt_config[] __initdata = { { OMAP_TAG_USB, &palmtt_usb_config }, { OMAP_TAG_LCD, &palmtt_lcd_config }, { OMAP_TAG_UART, &palmtt_uart_config }, @@ -338,6 +338,7 @@ static void __init omap_palmtt_init(void) spi_register_board_info(palmtt_boardinfo,ARRAY_SIZE(palmtt_boardinfo)); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init omap_palmtt_map_io(void) diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c index a9a0f6610c3..156510777ff 100644 --- a/arch/arm/mach-omap1/board-palmz71.c +++ b/arch/arm/mach-omap1/board-palmz71.c @@ -285,7 +285,7 @@ static struct omap_uart_config palmz71_uart_config __initdata = { .enabled_uarts = (1 << 0) | (1 << 1) | (0 << 2), }; -static struct omap_board_config_kernel palmz71_config[] = { +static struct omap_board_config_kernel palmz71_config[] __initdata = { {OMAP_TAG_USB, &palmz71_usb_config}, {OMAP_TAG_MMC, &palmz71_mmc_config}, {OMAP_TAG_LCD, &palmz71_lcd_config}, @@ -363,6 +363,7 @@ omap_palmz71_init(void) spi_register_board_info(palmz71_boardinfo, ARRAY_SIZE(palmz71_boardinfo)); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); palmz71_gpio_setup(0); } diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 534dcfb9d26..94bc0745ab2 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -30,6 +30,7 @@ #include <asm/arch/gpio.h> #include <asm/arch/mux.h> #include <asm/arch/fpga.h> +#include <asm/arch/nand.h> #include <asm/arch/keypad.h> #include <asm/arch/common.h> #include <asm/arch/board.h> @@ -133,7 +134,7 @@ static struct platform_device nor_device = { .resource = &nor_resource, }; -static struct nand_platform_data nand_data = { +static struct omap_nand_platform_data nand_data = { .options = NAND_SAMSUNG_LP_OPTIONS, }; @@ -202,7 +203,7 @@ static struct platform_device *devices[] __initdata = { #define P2_NAND_RB_GPIO_PIN 62 -static int nand_dev_ready(struct nand_platform_data *data) +static int nand_dev_ready(struct omap_nand_platform_data *data) { return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN); } @@ -215,7 +216,7 @@ static struct omap_lcd_config perseus2_lcd_config __initdata = { .ctrl_name = "internal", }; -static struct omap_board_config_kernel perseus2_config[] = { +static struct omap_board_config_kernel perseus2_config[] __initdata = { { OMAP_TAG_UART, &perseus2_uart_config }, { OMAP_TAG_LCD, &perseus2_lcd_config }, }; @@ -233,6 +234,7 @@ static void __init omap_perseus2_init(void) omap_board_config = perseus2_config; omap_board_config_size = ARRAY_SIZE(perseus2_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); } static void __init perseus2_init_smc91x(void) 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 00000000000..8c93d47719e --- /dev/null +++ b/arch/arm/mach-omap1/board-sx1-mmc.c @@ -0,0 +1,124 @@ +/* + * 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 + * + * 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. + */ + +#include <asm/arch/hardware.h> +#include <asm/arch/mmc.h> +#include <asm/arch/gpio.h> + +#ifdef CONFIG_MMC_OMAP +static int slot_cover_open; +static struct device *mmc_device; + +static int sx1_mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + int err; + u8 dat = 0; + +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1, + power_on ? "on" : "off", vdd); +#endif + + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + 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); +} + +static int sx1_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode) +{ +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1, + bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull"); +#endif + if (slot != 0) { + dev_err(dev, "No such slot %d\n", slot + 1); + return -ENODEV; + } + + return 0; +} + +static int sx1_mmc_get_cover_state(struct device *dev, int slot) +{ + BUG_ON(slot != 0); + + return slot_cover_open; +} + +void sx1_mmc_slot_cover_handler(void *arg, int state) +{ + if (mmc_device == NULL) + return; + + slot_cover_open = state; + omap_mmc_notify_cover_event(mmc_device, 0, state); +} + +static int sx1_mmc_late_init(struct device *dev) +{ + int ret = 0; + + mmc_device = dev; + + return ret; +} + +static void sx1_mmc_cleanup(struct device *dev) +{ +} + +static struct omap_mmc_platform_data sx1_mmc_data = { + .nr_slots = 1, + .switch_slot = NULL, + .init = sx1_mmc_late_init, + .cleanup = sx1_mmc_cleanup, + .slots[0] = { + .set_power = sx1_mmc_set_power, + .set_bus_mode = sx1_mmc_set_bus_mode, + .get_ro = NULL, + .get_cover_state = sx1_mmc_get_cover_state, + .ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 | + MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "mmcblk", + }, +}; + +void __init sx1_mmc_init(void) +{ + omap_set_mmc_info(1, &sx1_mmc_data); +} + +#else + +void __init sx1_mmc_init(void) +{ +} + +void sx1_mmc_slot_cover_handler(void *arg, int state) +{ +} +#endif diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c index 2743d639aa0..1c7f09aedf0 100644 --- a/arch/arm/mach-omap1/board-sx1.c +++ b/arch/arm/mach-omap1/board-sx1.c @@ -44,7 +44,7 @@ #include <asm/arch/keypad.h> /* Write to I2C device */ -int i2c_write_byte(u8 devaddr, u8 regoffset, u8 value) +int sx1_i2c_write_byte(u8 devaddr, u8 regoffset, u8 value) { struct i2c_adapter *adap; int err; @@ -67,7 +67,7 @@ int i2c_write_byte(u8 devaddr, u8 regoffset, u8 value) } /* Read from I2C device */ -int i2c_read_byte(u8 devaddr, u8 regoffset, u8 * value) +int sx1_i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value) { struct i2c_adapter *adap; int err; @@ -101,66 +101,55 @@ int sx1_setkeylight(u8 keylight) { if (keylight > SOFIA_MAX_LIGHT_VAL) keylight = SOFIA_MAX_LIGHT_VAL; - return i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_KEYLIGHT_REG, keylight); + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_KEYLIGHT_REG, keylight); } /* get current keylight intensity */ int sx1_getkeylight(u8 * keylight) { - return i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_KEYLIGHT_REG, 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 i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_BACKLIGHT_REG, backlight); + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_BACKLIGHT_REG, + backlight); } /* get current LCD backlight intensity */ int sx1_getbacklight (u8 * backlight) { - return i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_BACKLIGHT_REG, 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 = i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); + 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 i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); -} -/* set MMC power on/off */ -int sx1_setmmcpower(u8 onoff) -{ - int err; - u8 dat = 0; - err = i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); - if (err < 0) - return err; - if (onoff) - dat |= SOFIA_MMC_POWER; - else - dat &= ~SOFIA_MMC_POWER; - return i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); + 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 = i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat); + 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 i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); + return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat); } EXPORT_SYMBOL(sx1_setkeylight); @@ -168,7 +157,6 @@ EXPORT_SYMBOL(sx1_getkeylight); EXPORT_SYMBOL(sx1_setbacklight); EXPORT_SYMBOL(sx1_getbacklight); EXPORT_SYMBOL(sx1_setmmipower); -EXPORT_SYMBOL(sx1_setmmcpower); EXPORT_SYMBOL(sx1_setusbpower); /*----------- Keypad -------------------------*/ @@ -280,21 +268,6 @@ static struct omap_mcbsp_reg_cfg mcbsp1_regs = { /* PCR0 =0f0f */ }; -/* TODO: PCM interface - McBSP2 */ -static struct omap_mcbsp_reg_cfg mcbsp2_regs = { - .spcr2 = FRST | GRST | XRST | XINTM(3), /* SPCR2=F1 */ - .spcr1 = RINTM(3) | RRST, /* SPCR1=30 */ - .rcr2 = 0, /* RCR2 =00 */ - .rcr1 = RFRLEN1(1) | RWDLEN1(OMAP_MCBSP_WORD_16), /* RCR1 = 140 */ - .xcr2 = 0, /* XCR2 = 0 */ - .xcr1 = XFRLEN1(1) | XWDLEN1(OMAP_MCBSP_WORD_16), /* XCR1 = 140 */ - .srgr1 = FWID(15) | CLKGDV(12), /* SRGR1=0f0c */ - .srgr2 = FSGM | FPER(31), /* SRGR2=101f */ - .pcr0 = FSXM | FSRM | CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP, - /* PCR0=0f0f */ - /* mcbsp: slave */ -}; - static struct omap_alsa_codec_config sx1_alsa_config = { .name = "SX1 EGold", .mcbsp_regs_alsa = &mcbsp1_regs, @@ -407,11 +380,8 @@ static struct omap_usb_config sx1_usb_config __initdata = { static struct omap_mmc_config sx1_mmc_config __initdata = { .mmc [0] = { - .enabled = 1, + .enabled = 1, .wire4 = 0, - .wp_pin = -1, - .power_pin = -1, /* power is in Sofia */ - .switch_pin = OMAP_MPUIO(3), }, }; @@ -440,13 +410,15 @@ static struct omap_uart_config sx1_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; -static struct omap_board_config_kernel sx1_config[] = { +static struct omap_board_config_kernel sx1_config[] __initdata = { { OMAP_TAG_USB, &sx1_usb_config }, { OMAP_TAG_MMC, &sx1_mmc_config }, { OMAP_TAG_LCD, &sx1_lcd_config }, { OMAP_TAG_UART, &sx1_uart_config }, }; + /*-----------------------------------------*/ + static void __init omap_sx1_init(void) { platform_add_devices(sx1_devices, ARRAY_SIZE(sx1_devices)); @@ -454,6 +426,8 @@ static void __init omap_sx1_init(void) omap_board_config = sx1_config; omap_board_config_size = ARRAY_SIZE(sx1_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); + sx1_mmc_init(); /* turn on USB power */ /* sx1_setusbpower(1); cant do it here because i2c is not ready */ diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index c82a1cd20ad..5c00b3f39cd 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c @@ -34,9 +34,6 @@ #include <asm/arch/tc.h> #include <asm/arch/usb.h> -extern void omap_init_time(void); -extern int omap_gpio_init(void); - static struct plat_serial8250_port voiceblue_ports[] = { { .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x40000), @@ -198,6 +195,7 @@ static void __init voiceblue_init(void) omap_board_config = voiceblue_config; omap_board_config_size = ARRAY_SIZE(voiceblue_config); omap_serial_init(); + omap_register_i2c_bus(1, 100, NULL, 0); /* There is a good chance board is going up, so enable power LED * (it is connected through invertor) */ diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 5d9faa68d2e..4ea2933f887 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c @@ -1,4 +1,3 @@ -//kernel/linux-omap-fsample/arch/arm/mach-omap1/clock.c#2 - edit change 3808 (text) /* * linux/arch/arm/mach-omap1/clock.c * @@ -650,9 +649,9 @@ static void __init omap1_clk_disable_unused(struct clk *clk) /* FIXME: This clock seems to be necessary but no-one * has asked for its activation. */ - if (clk == &tc2_ck // FIX: pm.c (SRAM), CCP, Camera - || clk == &ck_dpll1out.clk // FIX: SoSSI, SSR - || clk == &arm_gpio_ck // FIX: GPIO code for 1510 + if (clk == &tc2_ck /* FIX: pm.c (SRAM), CCP, Camera */ + || clk == &ck_dpll1out.clk /* FIX: SoSSI, SSR */ + || clk == &arm_gpio_ck /* FIX: GPIO code for 1510 */ ) { printk(KERN_INFO "FIXME: Clock \"%s\" seems unused\n", clk->name); diff --git a/arch/arm/mach-omap1/leds-osk.c b/arch/arm/mach-omap1/leds-osk.c index 6939d5e7569..026685ed461 100644 --- a/arch/arm/mach-omap1/leds-osk.c +++ b/arch/arm/mach-omap1/leds-osk.c @@ -82,7 +82,7 @@ static void mistral_setled(void) red = 1; else if (hw_led_state & IDLE_LED) green = 1; - // else both sides are disabled + /* else both sides are disabled */ omap_set_gpio_dataout(GPIO_LED_GREEN, green); omap_set_gpio_dataout(GPIO_LED_RED, red); @@ -112,7 +112,7 @@ void osk_leds_event(led_event_t evt) case led_stop: led_state &= ~LED_STATE_ENABLED; hw_led_state = 0; - // NOTE: work may still be pending!! + /* NOTE: work may still be pending!! */ break; case led_claim: diff --git a/arch/arm/mach-omap1/mailbox.c b/arch/arm/mach-omap1/mailbox.c index d3abf560990..bad1e7152d8 100644 --- a/arch/arm/mach-omap1/mailbox.c +++ b/arch/arm/mach-omap1/mailbox.c @@ -51,7 +51,7 @@ static inline void mbox_write_reg(unsigned int val, unsigned int reg) } /* msg */ -static inline mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox) +static mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox) { struct omap_mbox1_fifo *fifo = &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; @@ -63,7 +63,7 @@ static inline mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox) return msg; } -static inline void +static void omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) { struct omap_mbox1_fifo *fifo = @@ -73,12 +73,12 @@ omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) mbox_write_reg(msg >> 16, fifo->cmd); } -static inline int omap1_mbox_fifo_empty(struct omap_mbox *mbox) +static int omap1_mbox_fifo_empty(struct omap_mbox *mbox) { return 0; } -static inline int omap1_mbox_fifo_full(struct omap_mbox *mbox) +static int omap1_mbox_fifo_full(struct omap_mbox *mbox) { struct omap_mbox1_fifo *fifo = &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; @@ -87,21 +87,21 @@ static inline int omap1_mbox_fifo_full(struct omap_mbox *mbox) } /* irq */ -static inline void +static void omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) { if (irq == IRQ_RX) enable_irq(mbox->irq); } -static inline void +static void omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) { if (irq == IRQ_RX) disable_irq(mbox->irq); } -static inline int +static int omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) { if (irq == IRQ_TX) diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c index 06b7e54a012..8eb5dcdaead 100644 --- a/arch/arm/mach-omap1/pm.c +++ b/arch/arm/mach-omap1/pm.c @@ -57,7 +57,6 @@ #include <asm/arch/pm.h> #include <asm/arch/mux.h> #include <asm/arch/dma.h> -#include <asm/arch/dsp_common.h> #include <asm/arch/dmtimer.h> static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; @@ -67,6 +66,8 @@ static unsigned int mpui730_sleep_save[MPUI730_SLEEP_SAVE_SIZE]; static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; +#ifdef CONFIG_OMAP_32K_TIMER + static unsigned short enable_dyn_sleep = 1; static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr, @@ -91,7 +92,8 @@ static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr, static struct kobj_attribute sleep_while_idle_attr = __ATTR(sleep_while_idle, 0644, idle_show, idle_store); -static void (*omap_sram_idle)(void) = NULL; +#endif + static void (*omap_sram_suspend)(unsigned long r0, unsigned long r1) = NULL; /* @@ -104,9 +106,7 @@ void omap_pm_idle(void) { extern __u32 arm_idlect1_mask; __u32 use_idlect1 = arm_idlect1_mask; -#ifndef CONFIG_OMAP_MPU_TIMER - int do_sleep; -#endif + int do_sleep = 0; local_irq_disable(); local_fiq_disable(); @@ -128,7 +128,6 @@ void omap_pm_idle(void) use_idlect1 = use_idlect1 & ~(1 << 9); #else - do_sleep = 0; while (enable_dyn_sleep) { #ifdef CONFIG_CBUS_TAHVO_USB @@ -141,6 +140,8 @@ void omap_pm_idle(void) break; } +#endif + #ifdef CONFIG_OMAP_DM_TIMER use_idlect1 = omap_dm_timer_modify_idlect_mask(use_idlect1); #endif @@ -168,7 +169,6 @@ void omap_pm_idle(void) } omap_sram_suspend(omap_readl(ARM_IDLECT1), omap_readl(ARM_IDLECT2)); -#endif local_fiq_enable(); local_irq_enable(); @@ -661,7 +661,10 @@ static struct platform_suspend_ops omap_pm_ops ={ static int __init omap_pm_init(void) { + +#ifdef CONFIG_OMAP_32K_TIMER int error; +#endif printk("Power Management for TI OMAP.\n"); @@ -671,23 +674,17 @@ static int __init omap_pm_init(void) * memory the MPU can see when it wakes up. */ if (cpu_is_omap730()) { - omap_sram_idle = omap_sram_push(omap730_idle_loop_suspend, - omap730_idle_loop_suspend_sz); omap_sram_suspend = omap_sram_push(omap730_cpu_suspend, omap730_cpu_suspend_sz); } else if (cpu_is_omap15xx()) { - omap_sram_idle = omap_sram_push(omap1510_idle_loop_suspend, - omap1510_idle_loop_suspend_sz); omap_sram_suspend = omap_sram_push(omap1510_cpu_suspend, omap1510_cpu_suspend_sz); } else if (cpu_is_omap16xx()) { - omap_sram_idle = omap_sram_push(omap1610_idle_loop_suspend, - omap1610_idle_loop_suspend_sz); omap_sram_suspend = omap_sram_push(omap1610_cpu_suspend, omap1610_cpu_suspend_sz); } - if (omap_sram_idle == NULL || omap_sram_suspend == NULL) { + if (omap_sram_suspend == NULL) { printk(KERN_ERR "PM not initialized: Missing SRAM support\n"); return -ENODEV; } @@ -719,9 +716,11 @@ static int __init omap_pm_init(void) omap_pm_init_proc(); #endif +#ifdef CONFIG_OMAP_32K_TIMER error = sysfs_create_file(power_kobj, &sleep_while_idle_attr); if (error) printk(KERN_ERR "sysfs_create_file failed: %d\n", error); +#endif if (cpu_is_omap16xx()) { /* configure LOW_PWR pin */ diff --git a/arch/arm/mach-omap1/sleep.S b/arch/arm/mach-omap1/sleep.S index abef33d10f0..68f5b39030b 100644 --- a/arch/arm/mach-omap1/sleep.S +++ b/arch/arm/mach-omap1/sleep.S @@ -39,167 +39,6 @@ .text -/* - * Forces OMAP into idle state - * - * omapXXXX_idle_loop_suspend() - * - * 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 slightly different configuration values we have - * processor specific functions here. - */ - -#if defined(CONFIG_ARCH_OMAP730) -ENTRY(omap730_idle_loop_suspend) - - stmfd sp!, {r0 - r12, lr} @ save registers on stack - - @ 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 - @ get ARM_IDLECT2 into r2 - ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - mov r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff - orr r5, r5, #OMAP730_IDLECT2_SLEEP_VAL & 0xff00 - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ request ARM idle - @ get ARM_IDLECT1 into r1 - ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - orr r3, r1, #OMAP730_IDLE_LOOP_REQUEST & 0xffff - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_730: subs r5, r5, #1 - bne l_730 -/* - * Let's wait for the next clock tick to wake us up. - */ - mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt -/* - * omap730_idle_loop_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. - */ - - @ restore ARM_IDLECT1 and ARM_IDLECT2 and return - @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 - strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - ldmfd sp!, {r0 - r12, pc} @ restore regs and return - -ENTRY(omap730_idle_loop_suspend_sz) - .word . - omap730_idle_loop_suspend -#endif /* CONFIG_ARCH_OMAP730 */ - -#ifdef CONFIG_ARCH_OMAP15XX -ENTRY(omap1510_idle_loop_suspend) - - stmfd sp!, {r0 - r12, lr} @ save registers on stack - - @ 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 - @ get ARM_IDLECT2 into r2 - ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - 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 - @ get ARM_IDLECT1 into r1 - ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1510: subs r5, r5, #1 - bne l_1510 -/* - * Let's wait for the next clock tick to wake us up. - */ - mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt -/* - * omap1510_idle_loop_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. - */ - - @ restore ARM_IDLECT1 and ARM_IDLECT2 and return - @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 - strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - ldmfd sp!, {r0 - r12, pc} @ restore regs and return - -ENTRY(omap1510_idle_loop_suspend_sz) - .word . - omap1510_idle_loop_suspend -#endif /* CONFIG_ARCH_OMAP15XX */ - -#if defined(CONFIG_ARCH_OMAP16XX) -ENTRY(omap1610_idle_loop_suspend) - - stmfd sp!, {r0 - r12, lr} @ save registers on stack - - @ 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 - @ get ARM_IDLECT2 into r2 - ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - 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 - @ get ARM_IDLECT1 into r1 - ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1610: subs r5, r5, #1 - bne l_1610 -/* - * Let's wait for the next clock tick to wake us up. - */ - mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt -/* - * omap1610_idle_loop_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. - */ - - @ restore ARM_IDLECT1 and ARM_IDLECT2 and return - @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 - strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - ldmfd sp!, {r0 - r12, pc} @ restore regs and return - -ENTRY(omap1610_idle_loop_suspend_sz) - .word . - omap1610_idle_loop_suspend -#endif /* CONFIG_ARCH_OMAP16XX */ /* * Forces OMAP into deep sleep state diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index e47e67c11af..7cd9ef8deb0 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -266,8 +266,6 @@ static void pxa3xx_cpu_standby(unsigned int pwrmode) AD2D0ER = 0; AD2D1ER = 0; - - printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR); } /* @@ -515,6 +513,14 @@ static int __init pxa3xx_init(void) int i, ret = 0; if (cpu_is_pxa3xx()) { + /* + * clear RDH bit every time after reset + * + * Note: the last 3 bits DxS are write-1-to-clear so carefully + * preserve them here in case they will be referenced later + */ + ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S); + clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks)); if ((ret = pxa_init_dma(32))) diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 2549129aabc..ce17df31b84 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_CPU_FREQ) += cpu-omap.o obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o +obj-$(CONFIG_I2C_OMAP) += i2c.o diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index dcbba07cf98..a46676db811 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -6,7 +6,7 @@ * 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 support Copyright (C) 2004-2005 Texas Instruments, Inc. + * 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. * @@ -33,12 +33,14 @@ #include <asm/arch/tc.h> -#define DEBUG_PRINTS -#undef DEBUG_PRINTS -#ifdef DEBUG_PRINTS -#define debug_printk(x) printk x -#else -#define debug_printk(x) +#undef DEBUG + +#ifndef CONFIG_ARCH_OMAP1 +enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED, + DMA_CH_QUEUED, DMA_CH_NOTSTARTED, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED +}; + +enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; #endif #define OMAP_DMA_ACTIVE 0x01 @@ -57,9 +59,66 @@ struct omap_dma_lch { const char *dev_name; void (* callback)(int lch, u16 ch_status, void *data); void *data; + +#ifndef CONFIG_ARCH_OMAP1 + /* required for Dynamic chaining */ + int prev_linked_ch; + int next_linked_ch; + int state; + int chain_id; + + int status; +#endif long flags; }; +#ifndef CONFIG_ARCH_OMAP1 +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 struct dma_link_info dma_linked_lch[OMAP_LOGICAL_DMA_CH_COUNT]; + +/* Chain handling macros */ +#define OMAP_DMA_CHAIN_QINIT(chain_id) \ + do { \ + dma_linked_lch[chain_id].q_head = \ + dma_linked_lch[chain_id].q_tail = \ + dma_linked_lch[chain_id].q_count = 0; \ + } while (0) +#define OMAP_DMA_CHAIN_QFULL(chain_id) \ + (dma_linked_lch[chain_id].no_of_lchs_linked == \ + dma_linked_lch[chain_id].q_count) +#define OMAP_DMA_CHAIN_QLAST(chain_id) \ + do { \ + ((dma_linked_lch[chain_id].no_of_lchs_linked-1) == \ + dma_linked_lch[chain_id].q_count) \ + } while (0) +#define OMAP_DMA_CHAIN_QEMPTY(chain_id) \ + (0 == dma_linked_lch[chain_id].q_count) +#define __OMAP_DMA_CHAIN_INCQ(end) \ + ((end) = ((end)+1) % dma_linked_lch[chain_id].no_of_lchs_linked) +#define OMAP_DMA_CHAIN_INCQHEAD(chain_id) \ + do { \ + __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_head); \ + dma_linked_lch[chain_id].q_count--; \ + } while (0) + +#define OMAP_DMA_CHAIN_INCQTAIL(chain_id) \ + do { \ + __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_tail); \ + dma_linked_lch[chain_id].q_count++; \ + } while (0) +#endif static int dma_chan_count; static spinlock_t dma_chan_lock; @@ -73,6 +132,10 @@ static const u8 omap1_dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = { INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD }; +static inline void disable_lnk(int lch); +static void omap_disable_channel_irq(int lch); +static inline void omap_enable_channel_irq(int lch); + #define REVISIT_24XX() printk(KERN_ERR "FIXME: no %s on 24xx\n", \ __FUNCTION__); @@ -148,7 +211,7 @@ void omap_set_dma_priority(int lch, int dst_port, int priority) omap_writel(l, reg); } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { if (priority) OMAP_DMA_CCR_REG(lch) |= (1 << 6); else @@ -173,7 +236,7 @@ void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, OMAP1_DMA_CCR2_REG(lch) |= 1 << 2; } - if (cpu_is_omap24xx() && dma_trigger) { + if (cpu_class_is_omap2() && dma_trigger) { u32 val = OMAP_DMA_CCR_REG(lch); val &= ~(3 << 19); @@ -213,7 +276,7 @@ void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) BUG_ON(omap_dma_in_1510_mode()); - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { REVISIT_24XX(); return; } @@ -245,7 +308,7 @@ void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode) { - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { OMAP_DMA_CSDP_REG(lch) &= ~(0x3 << 16); OMAP_DMA_CSDP_REG(lch) |= (mode << 16); } @@ -269,7 +332,7 @@ void omap_set_dma_src_params(int lch, int src_port, int src_amode, OMAP1_DMA_CSSA_L_REG(lch) = src_start; } - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) OMAP2_DMA_CSSA_REG(lch) = src_start; OMAP_DMA_CSEI_REG(lch) = src_ei; @@ -289,11 +352,14 @@ void omap_set_dma_params(int lch, struct omap_dma_channel_params * params) omap_set_dma_dest_params(lch, params->dst_port, params->dst_amode, params->dst_start, params->dst_ei, params->dst_fi); + if (params->read_prio || params->write_prio) + omap_dma_set_prio_lch(lch, params->read_prio, + params->write_prio); } void omap_set_dma_src_index(int lch, int eidx, int fidx) { - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { REVISIT_24XX(); return; } @@ -317,13 +383,13 @@ void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) case OMAP_DMA_DATA_BURST_DIS: break; case OMAP_DMA_DATA_BURST_4: - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) burst = 0x1; else burst = 0x2; break; case OMAP_DMA_DATA_BURST_8: - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { burst = 0x2; break; } @@ -332,7 +398,7 @@ void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) * fall through */ case OMAP_DMA_DATA_BURST_16: - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { burst = 0x3; break; } @@ -363,7 +429,7 @@ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, OMAP1_DMA_CDSA_L_REG(lch) = dest_start; } - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) OMAP2_DMA_CDSA_REG(lch) = dest_start; OMAP_DMA_CDEI_REG(lch) = dst_ei; @@ -372,7 +438,7 @@ void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, void omap_set_dma_dest_index(int lch, int eidx, int fidx) { - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { REVISIT_24XX(); return; } @@ -396,19 +462,19 @@ void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) case OMAP_DMA_DATA_BURST_DIS: break; case OMAP_DMA_DATA_BURST_4: - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) burst = 0x1; else burst = 0x2; break; case OMAP_DMA_DATA_BURST_8: - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) burst = 0x2; else burst = 0x3; break; case OMAP_DMA_DATA_BURST_16: - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { burst = 0x3; break; } @@ -430,7 +496,7 @@ static inline void omap_enable_channel_irq(int lch) /* Clear CSR */ if (cpu_class_is_omap1()) status = OMAP_DMA_CSR_REG(lch); - else if (cpu_is_omap24xx()) + else if (cpu_class_is_omap2()) OMAP_DMA_CSR_REG(lch) = OMAP2_DMA_CSR_CLEAR_MASK; /* Enable some nice interrupts. */ @@ -441,7 +507,7 @@ static inline void omap_enable_channel_irq(int lch) static void omap_disable_channel_irq(int lch) { - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) OMAP_DMA_CICR_REG(lch) = 0; } @@ -464,6 +530,12 @@ static inline void enable_lnk(int lch) if (dma_chan[lch].next_lch != -1) OMAP_DMA_CLNK_CTRL_REG(lch) = dma_chan[lch].next_lch | (1 << 15); + +#ifndef CONFIG_ARCH_OMAP1 + if (dma_chan[lch].next_linked_ch != -1) + OMAP_DMA_CLNK_CTRL_REG(lch) = + dma_chan[lch].next_linked_ch | (1 << 15); +#endif } static inline void disable_lnk(int lch) @@ -475,7 +547,7 @@ static inline void disable_lnk(int lch) OMAP_DMA_CLNK_CTRL_REG(lch) |= 1 << 14; } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { omap_disable_channel_irq(lch); /* Clear the ENABLE_LNK bit */ OMAP_DMA_CLNK_CTRL_REG(lch) &= ~(1 << 15); @@ -488,7 +560,7 @@ static inline void omap2_enable_irq_lch(int lch) { u32 val; - if (!cpu_is_omap24xx()) + if (!cpu_class_is_omap2()) return; val = omap_readl(OMAP_DMA4_IRQENABLE_L0); @@ -522,7 +594,7 @@ int omap_request_dma(int dev_id, const char *dev_name, if (cpu_class_is_omap1()) clear_lch_regs(free_ch); - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) omap_clear_dma(free_ch); spin_unlock_irqrestore(&dma_chan_lock, flags); @@ -530,11 +602,14 @@ int omap_request_dma(int dev_id, const char *dev_name, chan->dev_name = dev_name; chan->callback = callback; chan->data = data; +#ifndef CONFIG_ARCH_OMAP1 + chan->chain_id = -1; +#endif chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; if (cpu_class_is_omap1()) chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ; - else if (cpu_is_omap24xx()) + else if (cpu_class_is_omap2()) chan->enabled_irqs |= OMAP2_DMA_MISALIGNED_ERR_IRQ | OMAP2_DMA_TRANS_ERR_IRQ; @@ -551,7 +626,7 @@ int omap_request_dma(int dev_id, const char *dev_name, OMAP_DMA_CCR_REG(free_ch) = dev_id; } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { omap2_enable_irq_lch(free_ch); omap_enable_channel_irq(free_ch); @@ -588,7 +663,7 @@ void omap_free_dma(int lch) OMAP_DMA_CCR_REG(lch) = 0; } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { u32 val; /* Disable interrupts */ val = omap_readl(OMAP_DMA4_IRQENABLE_L0); @@ -608,6 +683,67 @@ void omap_free_dma(int lch) } } +/** + * @brief omap_dma_set_global_params : Set global priority settings for dma + * + * @param arb_rate + * @param max_fifo_depth + * @param tparams - Number of thereads to reserve : DMA_THREAD_RESERVE_NORM + * DMA_THREAD_RESERVE_ONET + * DMA_THREAD_RESERVE_TWOT + * DMA_THREAD_RESERVE_THREET + */ +void +omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams) +{ + u32 reg; + + if (!cpu_class_is_omap2()) { + printk(KERN_ERR "FIXME: no %s on 15xx/16xx\n", __FUNCTION__); + return; + } + + if (arb_rate == 0) + arb_rate = 1; + + reg = (arb_rate & 0xff) << 16; + reg |= (0xff & max_fifo_depth); + + omap_writel(reg, OMAP_DMA4_GCR_REG); +} +EXPORT_SYMBOL(omap_dma_set_global_params); + +/** + * @brief omap_dma_set_prio_lch : Set channel wise priority settings + * + * @param lch + * @param read_prio - Read priority + * @param write_prio - Write priority + * Both of the above can be set with one of the following values : + * DMA_CH_PRIO_HIGH/DMA_CH_PRIO_LOW + */ +int +omap_dma_set_prio_lch(int lch, unsigned char read_prio, + unsigned char write_prio) +{ + u32 w; + + if (unlikely((lch < 0 || lch >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid channel id\n"); + return -EINVAL; + } + w = OMAP_DMA_CCR_REG(lch); + w &= ~((1 << 6) | (1 << 26)); + if (cpu_is_omap2430() || cpu_is_omap34xx()) + w |= ((read_prio & 0x1) << 6) | ((write_prio & 0x1) << 26); + else + w |= ((read_prio & 0x1) << 6); + + OMAP_DMA_CCR_REG(lch) = w; + return 0; +} +EXPORT_SYMBOL(omap_dma_set_prio_lch); + /* * 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. @@ -626,9 +762,9 @@ void omap_clear_dma(int lch) status = OMAP_DMA_CSR_REG(lch); } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { int i; - u32 lch_base = OMAP24XX_DMA_BASE + lch * 0x60 + 0x80; + u32 lch_base = OMAP_DMA4_BASE + lch * 0x60 + 0x80; for (i = 0; i < 0x44; i += 4) omap_writel(0, lch_base + i); } @@ -662,7 +798,7 @@ void omap_start_dma(int lch) cur_lch = next_lch; } while (next_lch != -1); - } else if (cpu_is_omap24xx()) { + } else if (cpu_class_is_omap2()) { /* Errata: Need to write lch even if not using chaining */ OMAP_DMA_CLNK_CTRL_REG(lch) = lch; } @@ -753,7 +889,7 @@ dma_addr_t omap_get_dma_src_pos(int lch) offset = (dma_addr_t) (OMAP1_DMA_CSSA_L_REG(lch) | (OMAP1_DMA_CSSA_U_REG(lch) << 16)); - if (cpu_is_omap24xx()) + if (cpu_class_is_omap2()) offset = OMAP_DMA_CSAC_REG(lch); return offset; @@ -775,8 +911,8 @@ dma_addr_t omap_get_dma_dst_pos(int lch) offset = (dma_addr_t) (OMAP1_DMA_CDSA_L_REG(lch) | (OMAP1_DMA_CDSA_U_REG(lch) << 16)); - if (cpu_is_omap24xx()) - offset = OMAP2_DMA_CDSA_REG(lch); + if (cpu_class_is_omap2()) + offset = OMAP_DMA_CDAC_REG(lch); return offset; } @@ -859,6 +995,605 @@ void omap_dma_unlink_lch (int lch_head, int lch_queue) dma_chan[lch_head].next_lch = -1; } +#ifndef CONFIG_ARCH_OMAP1 +/* Create chain of DMA channesls */ +static void create_dma_lch_chain(int lch_head, int lch_queue) +{ + u32 w; + + /* Check if this is the first link in chain */ + if (dma_chan[lch_head].next_linked_ch == -1) { + dma_chan[lch_head].next_linked_ch = lch_queue; + dma_chan[lch_head].prev_linked_ch = lch_queue; + dma_chan[lch_queue].next_linked_ch = lch_head; + dma_chan[lch_queue].prev_linked_ch = lch_head; + } + + /* a link exists, link the new channel in circular chain */ + else { + dma_chan[lch_queue].next_linked_ch = + dma_chan[lch_head].next_linked_ch; + dma_chan[lch_queue].prev_linked_ch = lch_head; + dma_chan[lch_head].next_linked_ch = lch_queue; + dma_chan[dma_chan[lch_queue].next_linked_ch].prev_linked_ch = + lch_queue; + } + + w = OMAP_DMA_CLNK_CTRL_REG(lch_head); + w &= ~(0x0f); + w |= lch_queue; + OMAP_DMA_CLNK_CTRL_REG(lch_head) = w; + + w = OMAP_DMA_CLNK_CTRL_REG(lch_queue); + w &= ~(0x0f); + w |= (dma_chan[lch_queue].next_linked_ch); + OMAP_DMA_CLNK_CTRL_REG(lch_queue) = w; +} + +/** + * @brief omap_request_dma_chain : Request a chain of DMA channels + * + * @param dev_id - Device id using the dma channel + * @param dev_name - Device name + * @param callback - Call back function + * @chain_id - + * @no_of_chans - Number of channels requested + * @chain_mode - Dynamic or static chaining : OMAP_DMA_STATIC_CHAIN + * OMAP_DMA_DYNAMIC_CHAIN + * @params - Channel parameters + * + * @return - Succes : 0 + * Failure: -EINVAL/-ENOMEM + */ +int omap_request_dma_chain(int dev_id, const char *dev_name, + void (*callback) (int chain_id, u16 ch_status, + void *data), + int *chain_id, int no_of_chans, int chain_mode, + struct omap_dma_channel_params params) +{ + int *channels; + int i, err; + + /* Is the chain mode valid ? */ + if (chain_mode != OMAP_DMA_STATIC_CHAIN + && chain_mode != OMAP_DMA_DYNAMIC_CHAIN) { + printk(KERN_ERR "Invalid chain mode requested\n"); + return -EINVAL; + } + + if (unlikely((no_of_chans < 1 + || no_of_chans > OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid Number of channels requested\n"); + return -EINVAL; + } + + /* Allocate a queue to maintain the status of the channels + * in the chain */ + channels = kmalloc(sizeof(*channels) * no_of_chans, GFP_KERNEL); + if (channels == NULL) { + printk(KERN_ERR "omap_dma: No memory for channel queue\n"); + return -ENOMEM; + } + + /* request and reserve DMA channels for the chain */ + for (i = 0; i < no_of_chans; i++) { + err = omap_request_dma(dev_id, dev_name, + callback, 0, &channels[i]); + if (err < 0) { + int j; + for (j = 0; j < i; j++) + omap_free_dma(channels[j]); + kfree(channels); + printk(KERN_ERR "omap_dma: Request failed %d\n", err); + return err; + } + dma_chan[channels[i]].next_linked_ch = -1; + dma_chan[channels[i]].prev_linked_ch = -1; + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + + /* + * Allowing client drivers to set common parameters now, + * so that later only relevant (src_start, dest_start + * and element count) can be set + */ + omap_set_dma_params(channels[i], ¶ms); + } + + *chain_id = channels[0]; + dma_linked_lch[*chain_id].linked_dmach_q = channels; + dma_linked_lch[*chain_id].chain_mode = chain_mode; + dma_linked_lch[*chain_id].chain_state = DMA_CHAIN_NOTSTARTED; + dma_linked_lch[*chain_id].no_of_lchs_linked = no_of_chans; + + for (i = 0; i < no_of_chans; i++) + dma_chan[channels[i]].chain_id = *chain_id; + + /* Reset the Queue pointers */ + OMAP_DMA_CHAIN_QINIT(*chain_id); + + /* Set up the chain */ + if (no_of_chans == 1) + create_dma_lch_chain(channels[0], channels[0]); + else { + for (i = 0; i < (no_of_chans - 1); i++) + create_dma_lch_chain(channels[i], channels[i + 1]); + } + return 0; +} +EXPORT_SYMBOL(omap_request_dma_chain); + +/** + * @brief omap_modify_dma_chain_param : Modify the chain's params - Modify the + * params after setting it. Dont do this while dma is running!! + * + * @param chain_id - Chained logical channel id. + * @param params + * + * @return - Success : 0 + * Failure : -EINVAL + */ +int omap_modify_dma_chain_params(int chain_id, + struct omap_dma_channel_params params) +{ + int *channels; + u32 i; + + /* Check for input params */ + if (unlikely((chain_id < 0 + || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + channels = dma_linked_lch[chain_id].linked_dmach_q; + + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + /* + * Allowing client drivers to set common parameters now, + * so that later only relevant (src_start, dest_start + * and element count) can be set + */ + omap_set_dma_params(channels[i], ¶ms); + } + return 0; +} +EXPORT_SYMBOL(omap_modify_dma_chain_params); + +/** + * @brief omap_free_dma_chain - Free all the logical channels in a chain. + * + * @param chain_id + * + * @return - Success : 0 + * Failure : -EINVAL + */ +int omap_free_dma_chain(int chain_id) +{ + int *channels; + u32 i; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + dma_chan[channels[i]].next_linked_ch = -1; + dma_chan[channels[i]].prev_linked_ch = -1; + dma_chan[channels[i]].chain_id = -1; + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + omap_free_dma(channels[i]); + } + + kfree(channels); + + dma_linked_lch[chain_id].linked_dmach_q = NULL; + dma_linked_lch[chain_id].chain_mode = -1; + dma_linked_lch[chain_id].chain_state = -1; + return (0); +} +EXPORT_SYMBOL(omap_free_dma_chain); + +/** + * @brief omap_dma_chain_status - Check if the chain is in + * active / inactive state. + * @param chain_id + * + * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE + * Failure : -EINVAL + */ +int omap_dma_chain_status(int chain_id) +{ + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + pr_debug("CHAINID=%d, qcnt=%d\n", chain_id, + dma_linked_lch[chain_id].q_count); + + if (OMAP_DMA_CHAIN_QEMPTY(chain_id)) + return OMAP_DMA_CHAIN_INACTIVE; + return OMAP_DMA_CHAIN_ACTIVE; +} +EXPORT_SYMBOL(omap_dma_chain_status); + +/** + * @brief omap_dma_chain_a_transfer - Get a free channel from a chain, + * set the params and start the transfer. + * + * @param chain_id + * @param src_start - buffer start address + * @param dest_start - Dest address + * @param elem_count + * @param frame_count + * @param callbk_data - channel callback parameter data. + * + * @return - Success : start_dma status + * Failure: -EINVAL/-EBUSY + */ +int omap_dma_chain_a_transfer(int chain_id, int src_start, int dest_start, + int elem_count, int frame_count, void *callbk_data) +{ + int *channels; + u32 w, lch; + int start_dma = 0; + + /* if buffer size is less than 1 then there is + * no use of starting the chain */ + if (elem_count < 1) { + printk(KERN_ERR "Invalid buffer size\n"); + return -EINVAL; + } + + /* Check for input params */ + if (unlikely((chain_id < 0 + || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exist\n"); + return -EINVAL; + } + + /* Check if all the channels in chain are in use */ + if (OMAP_DMA_CHAIN_QFULL(chain_id)) + return -EBUSY; + + /* Frame count may be negative in case of indexed transfers */ + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get a free channel */ + lch = channels[dma_linked_lch[chain_id].q_tail]; + + /* Store the callback data */ + dma_chan[lch].data = callbk_data; + + /* Increment the q_tail */ + OMAP_DMA_CHAIN_INCQTAIL(chain_id); + + /* Set the params to the free channel */ + if (src_start != 0) + OMAP2_DMA_CSSA_REG(lch) = src_start; + if (dest_start != 0) + OMAP2_DMA_CDSA_REG(lch) = dest_start; + + /* Write the buffer size */ + OMAP_DMA_CEN_REG(lch) = elem_count; + OMAP_DMA_CFN_REG(lch) = frame_count; + + /* If the chain is dynamically linked, + * then we may have to start the chain if its not active */ + if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) { + + /* In Dynamic chain, if the chain is not started, + * queue the channel */ + if (dma_linked_lch[chain_id].chain_state == + DMA_CHAIN_NOTSTARTED) { + /* Enable the link in previous channel */ + if (dma_chan[dma_chan[lch].prev_linked_ch].state == + DMA_CH_QUEUED) + enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + } + + /* Chain is already started, make sure its active, + * if not then start the chain */ + else { + start_dma = 1; + + if (dma_chan[dma_chan[lch].prev_linked_ch].state == + DMA_CH_STARTED) { + enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + start_dma = 0; + if (0 == ((1 << 7) & (OMAP_DMA_CCR_REG + (dma_chan[lch].prev_linked_ch)))) { + disable_lnk(dma_chan[lch]. + prev_linked_ch); + pr_debug("\n prev ch is stopped\n"); + start_dma = 1; + } + } + + else if (dma_chan[dma_chan[lch].prev_linked_ch].state + == DMA_CH_QUEUED) { + enable_lnk(dma_chan[lch].prev_linked_ch); + dma_chan[lch].state = DMA_CH_QUEUED; + start_dma = 0; + } + omap_enable_channel_irq(lch); + + w = OMAP_DMA_CCR_REG(lch); + + if ((0 == (w & (1 << 24)))) + w &= ~(1 << 25); + else + w |= (1 << 25); + if (start_dma == 1) { + if (0 == (w & (1 << 7))) { + w |= (1 << 7); + dma_chan[lch].state = DMA_CH_STARTED; + pr_debug("starting %d\n", lch); + OMAP_DMA_CCR_REG(lch) = w; + } else + start_dma = 0; + } else { + if (0 == (w & (1 << 7))) + OMAP_DMA_CCR_REG(lch) = w; + } + dma_chan[lch].flags |= OMAP_DMA_ACTIVE; + } + } + return start_dma; +} +EXPORT_SYMBOL(omap_dma_chain_a_transfer); + +/** + * @brief omap_start_dma_chain_transfers - Start the chain + * + * @param chain_id + * + * @return - Success : 0 + * Failure : -EINVAL/-EBUSY + */ +int omap_start_dma_chain_transfers(int chain_id) +{ + int *channels; + u32 w, i; + + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) { + printk(KERN_ERR "Chain is already started\n"); + return -EBUSY; + } + + if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) { + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; + i++) { + enable_lnk(channels[i]); + omap_enable_channel_irq(channels[i]); + } + } else { + omap_enable_channel_irq(channels[0]); + } + + w = OMAP_DMA_CCR_REG(channels[0]); + w |= (1 << 7); + dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED; + dma_chan[channels[0]].state = DMA_CH_STARTED; + + if ((0 == (w & (1 << 24)))) + w &= ~(1 << 25); + else + w |= (1 << 25); + OMAP_DMA_CCR_REG(channels[0]) = w; + + dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE; + return 0; +} +EXPORT_SYMBOL(omap_start_dma_chain_transfers); + +/** + * @brief omap_stop_dma_chain_transfers - Stop the dma transfer of a chain. + * + * @param chain_id + * + * @return - Success : 0 + * Failure : EINVAL + */ +int omap_stop_dma_chain_transfers(int chain_id) +{ + int *channels; + u32 w, i; + u32 sys_cf; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* DMA Errata: + * Special programming model needed to disable DMA before end of block + */ + sys_cf = omap_readl(OMAP_DMA4_OCP_SYSCONFIG); + w = sys_cf; + /* Middle mode reg set no Standby */ + w &= ~((1 << 12)|(1 << 13)); + omap_writel(w, OMAP_DMA4_OCP_SYSCONFIG); + + for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) { + + /* Stop the Channel transmission */ + w = OMAP_DMA_CCR_REG(channels[i]); + w &= ~(1 << 7); + OMAP_DMA_CCR_REG(channels[i]) = w; + + /* Disable the link in all the channels */ + disable_lnk(channels[i]); + dma_chan[channels[i]].state = DMA_CH_NOTSTARTED; + + } + dma_linked_lch[chain_id].chain_state = DMA_CHAIN_NOTSTARTED; + + /* Reset the Queue pointers */ + OMAP_DMA_CHAIN_QINIT(chain_id); + + /* Errata - put in the old value */ + omap_writel(sys_cf, OMAP_DMA4_OCP_SYSCONFIG); + return 0; +} +EXPORT_SYMBOL(omap_stop_dma_chain_transfers); + +/* Get the index of the ongoing DMA in chain */ +/** + * @brief omap_get_dma_chain_index - Get the element and frame index + * of the ongoing DMA in chain + * + * @param chain_id + * @param ei - Element index + * @param fi - Frame index + * + * @return - Success : 0 + * Failure : -EINVAL + */ +int omap_get_dma_chain_index(int chain_id, int *ei, int *fi) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + if ((!ei) || (!fi)) + return -EINVAL; + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + *ei = OMAP2_DMA_CCEN_REG(lch); + *fi = OMAP2_DMA_CCFN_REG(lch); + + return 0; +} +EXPORT_SYMBOL(omap_get_dma_chain_index); + +/** + * @brief omap_get_dma_chain_dst_pos - Get the destination position of the + * ongoing DMA in chain + * + * @param chain_id + * + * @return - Success : Destination position + * Failure : -EINVAL + */ +int omap_get_dma_chain_dst_pos(int chain_id) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + return (OMAP_DMA_CDAC_REG(lch)); +} +EXPORT_SYMBOL(omap_get_dma_chain_dst_pos); + +/** + * @brief omap_get_dma_chain_src_pos - Get the source position + * of the ongoing DMA in chain + * @param chain_id + * + * @return - Success : Destination position + * Failure : -EINVAL + */ +int omap_get_dma_chain_src_pos(int chain_id) +{ + int lch; + int *channels; + + /* Check for input params */ + if (unlikely((chain_id < 0 || chain_id >= OMAP_LOGICAL_DMA_CH_COUNT))) { + printk(KERN_ERR "Invalid chain id\n"); + return -EINVAL; + } + + /* Check if the chain exists */ + if (dma_linked_lch[chain_id].linked_dmach_q == NULL) { + printk(KERN_ERR "Chain doesn't exists\n"); + return -EINVAL; + } + + channels = dma_linked_lch[chain_id].linked_dmach_q; + + /* Get the current channel */ + lch = channels[dma_linked_lch[chain_id].q_head]; + + return (OMAP_DMA_CSAC_REG(lch)); +} +EXPORT_SYMBOL(omap_get_dma_chain_src_pos); +#endif + /*----------------------------------------------------------------------------*/ #ifdef CONFIG_ARCH_OMAP1 @@ -919,7 +1654,7 @@ static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id) #define omap1_dma_irq_handler NULL #endif -#ifdef CONFIG_ARCH_OMAP2 +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) static int omap2_dma_handle_ch(int ch) { @@ -953,8 +1688,33 @@ static int omap2_dma_handle_ch(int ch) OMAP_DMA_CSR_REG(ch) = OMAP2_DMA_CSR_CLEAR_MASK; omap_writel(1 << ch, OMAP_DMA4_IRQSTATUS_L0); - if (likely(dma_chan[ch].callback != NULL)) - dma_chan[ch].callback(ch, status, dma_chan[ch].data); + /* If the ch is not chained then chain_id will be -1 */ + if (dma_chan[ch].chain_id != -1) { + int chain_id = dma_chan[ch].chain_id; + dma_chan[ch].state = DMA_CH_NOTSTARTED; + if (OMAP_DMA_CLNK_CTRL_REG(ch) & (1 << 15)) + dma_chan[dma_chan[ch].next_linked_ch].state = + DMA_CH_STARTED; + if (dma_linked_lch[chain_id].chain_mode == + OMAP_DMA_DYNAMIC_CHAIN) + disable_lnk(ch); + + if (!OMAP_DMA_CHAIN_QEMPTY(chain_id)) + OMAP_DMA_CHAIN_INCQHEAD(chain_id); + + status = OMAP_DMA_CSR_REG(ch); + } + + if (likely(dma_chan[ch].callback != NULL)) { + if (dma_chan[ch].chain_id != -1) + dma_chan[ch].callback(dma_chan[ch].chain_id, status, + dma_chan[ch].data); + else + dma_chan[ch].callback(ch, status, dma_chan[ch].data); + + } + + OMAP_DMA_CSR_REG(ch) = status; return 0; } @@ -1385,7 +2145,7 @@ static int __init omap_init_dma(void) w &= ~(1 << 8); omap_writew(w, OMAP1610_DMA_LCD_CTRL); } - } else if (cpu_is_omap24xx()) { + } else if (cpu_class_is_omap2()) { u8 revision = omap_readb(OMAP_DMA4_REVISION); printk(KERN_INFO "OMAP DMA hardware revision %d.%d\n", revision >> 4, revision & 0xf); @@ -1428,7 +2188,11 @@ static int __init omap_init_dma(void) } } - if (cpu_is_omap24xx()) + if (cpu_is_omap2430() || cpu_is_omap34xx()) + omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, + DMA_DEFAULT_FIFO_DEPTH, 0); + + if (cpu_class_is_omap2()) setup_irq(INT_24XX_SDMA_IRQ0, &omap24xx_dma_irq); /* FIXME: Update LCD DMA to work on 24xx */ diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 3856f5aedfc..e719d0eeb5c 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -48,7 +48,7 @@ #define OMAP_TIMER_COUNTER_REG 0x28 #define OMAP_TIMER_LOAD_REG 0x2c #define OMAP_TIMER_TRIGGER_REG 0x30 -#define OMAP_TIMER_WRITE_PEND_REG 0x34 +#define OMAP_TIMER_WRITE_PEND_REG 0x34 #define OMAP_TIMER_MATCH_REG 0x38 #define OMAP_TIMER_CAPTURE_REG 0x3c #define OMAP_TIMER_IF_CTRL_REG 0x40 @@ -70,7 +70,7 @@ struct omap_dm_timer { unsigned long phys_base; int irq; -#ifdef CONFIG_ARCH_OMAP2 +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) struct clk *iclk, *fclk; #endif void __iomem *io_base; @@ -82,8 +82,14 @@ struct omap_dm_timer { #define omap_dm_clk_enable(x) #define omap_dm_clk_disable(x) - -static struct omap_dm_timer dm_timers[] = { +#define omap2_dm_timers NULL +#define omap2_dm_source_names NULL +#define omap2_dm_source_clocks NULL +#define omap3_dm_timers NULL +#define omap3_dm_source_names NULL +#define omap3_dm_source_clocks NULL + +static struct omap_dm_timer omap1_dm_timers[] = { { .phys_base = 0xfffb1400, .irq = INT_1610_GPTIMER1 }, { .phys_base = 0xfffb1c00, .irq = INT_1610_GPTIMER2 }, { .phys_base = 0xfffb2400, .irq = INT_1610_GPTIMER3 }, @@ -94,12 +100,18 @@ static struct omap_dm_timer dm_timers[] = { { .phys_base = 0xfffbd400, .irq = INT_1610_GPTIMER8 }, }; +static const int dm_timer_count = ARRAY_SIZE(omap1_dm_timers); + #elif defined(CONFIG_ARCH_OMAP2) -#define omap_dm_clk_enable(x) clk_enable(x) -#define omap_dm_clk_disable(x) clk_disable(x) +#define omap_dm_clk_enable(x) clk_enable(x) +#define omap_dm_clk_disable(x) clk_disable(x) +#define omap1_dm_timers NULL +#define omap3_dm_timers NULL +#define omap3_dm_source_names NULL +#define omap3_dm_source_clocks NULL -static struct omap_dm_timer dm_timers[] = { +static struct omap_dm_timer omap2_dm_timers[] = { { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 }, { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 }, { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 }, @@ -114,13 +126,48 @@ static struct omap_dm_timer dm_timers[] = { { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 }, }; -static const char *dm_source_names[] = { +static const char *omap2_dm_source_names[] __initdata = { "sys_ck", "func_32k_ck", - "alt_ck" + "alt_ck", + NULL +}; + +static struct clk **omap2_dm_source_clocks[3]; +static const int dm_timer_count = ARRAY_SIZE(omap2_dm_timers); + +#elif defined(CONFIG_ARCH_OMAP3) + +#define omap_dm_clk_enable(x) clk_enable(x) +#define omap_dm_clk_disable(x) clk_disable(x) +#define omap1_dm_timers NULL +#define omap2_dm_timers NULL +#define omap2_dm_source_names NULL +#define omap2_dm_source_clocks NULL + +static struct omap_dm_timer omap3_dm_timers[] = { + { .phys_base = 0x48318000, .irq = INT_24XX_GPTIMER1 }, + { .phys_base = 0x49032000, .irq = INT_24XX_GPTIMER2 }, + { .phys_base = 0x49034000, .irq = INT_24XX_GPTIMER3 }, + { .phys_base = 0x49036000, .irq = INT_24XX_GPTIMER4 }, + { .phys_base = 0x49038000, .irq = INT_24XX_GPTIMER5 }, + { .phys_base = 0x4903A000, .irq = INT_24XX_GPTIMER6 }, + { .phys_base = 0x4903C000, .irq = INT_24XX_GPTIMER7 }, + { .phys_base = 0x4903E000, .irq = INT_24XX_GPTIMER8 }, + { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 }, + { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, + { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, + { .phys_base = 0x48304000, .irq = INT_24XX_GPTIMER12 }, +}; + +static const char *omap3_dm_source_names[] __initdata = { + "sys_ck", + "omap_32k_fck", + NULL }; -static struct clk *dm_source_clocks[3]; +static struct clk **omap3_dm_source_clocks[2]; +static const int dm_timer_count = ARRAY_SIZE(omap3_dm_timers); #else @@ -128,7 +175,10 @@ static struct clk *dm_source_clocks[3]; #endif -static const int dm_timer_count = ARRAY_SIZE(dm_timers); +static struct omap_dm_timer *dm_timers; +static char **dm_source_names; +static struct clk **dm_source_clocks; + static spinlock_t dm_timer_lock; static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg) @@ -299,7 +349,7 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) return inputmask; } -#elif defined(CONFIG_ARCH_OMAP2) +#elif defined(CONFIG_ARCH_OMAP2) || defined (CONFIG_ARCH_OMAP3) struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) { @@ -486,36 +536,46 @@ int omap_dm_timers_active(void) return 0; } -int omap_dm_timer_init(void) +int __init omap_dm_timer_init(void) { struct omap_dm_timer *timer; int i; - if (!(cpu_is_omap16xx() || cpu_is_omap24xx())) + if (!(cpu_is_omap16xx() || cpu_class_is_omap2())) return -ENODEV; spin_lock_init(&dm_timer_lock); -#ifdef CONFIG_ARCH_OMAP2 - for (i = 0; i < ARRAY_SIZE(dm_source_names); i++) { - dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]); - BUG_ON(dm_source_clocks[i] == NULL); + + if (cpu_class_is_omap1()) + dm_timers = omap1_dm_timers; + else if (cpu_is_omap24xx()) { + dm_timers = omap2_dm_timers; + dm_source_names = (char **)omap2_dm_source_names; + dm_source_clocks = (struct clk **)omap2_dm_source_clocks; + } else if (cpu_is_omap34xx()) { + dm_timers = omap3_dm_timers; + dm_source_names = (char **)omap3_dm_source_names; + dm_source_clocks = (struct clk **)omap3_dm_source_clocks; } -#endif + + if (cpu_class_is_omap2()) + for (i = 0; dm_source_names[i] != NULL; i++) + dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]); + if (cpu_is_omap243x()) dm_timers[0].phys_base = 0x49018000; for (i = 0; i < dm_timer_count; i++) { -#ifdef CONFIG_ARCH_OMAP2 - char clk_name[16]; -#endif - timer = &dm_timers[i]; - timer->io_base = (void __iomem *) io_p2v(timer->phys_base); -#ifdef CONFIG_ARCH_OMAP2 - sprintf(clk_name, "gpt%d_ick", i + 1); - timer->iclk = clk_get(NULL, clk_name); - sprintf(clk_name, "gpt%d_fck", i + 1); - timer->fclk = clk_get(NULL, clk_name); + timer->io_base = (void __iomem *)io_p2v(timer->phys_base); +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + if (cpu_class_is_omap2()) { + char clk_name[16]; + sprintf(clk_name, "gpt%d_ick", i + 1); + timer->iclk = clk_get(NULL, clk_name); + sprintf(clk_name, "gpt%d_fck", i + 1); + timer->fclk = clk_get(NULL, clk_name); + } #endif } diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index b2a87b8ef67..56f4d1394d5 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -110,6 +110,8 @@ #define OMAP24XX_GPIO_LEVELDETECT1 0x0044 #define OMAP24XX_GPIO_RISINGDETECT 0x0048 #define OMAP24XX_GPIO_FALLINGDETECT 0x004c +#define OMAP24XX_GPIO_DEBOUNCE_EN 0x0050 +#define OMAP24XX_GPIO_DEBOUNCE_VAL 0x0054 #define OMAP24XX_GPIO_CLEARIRQENABLE1 0x0060 #define OMAP24XX_GPIO_SETIRQENABLE1 0x0064 #define OMAP24XX_GPIO_CLEARWKUENA 0x0080 @@ -117,17 +119,29 @@ #define OMAP24XX_GPIO_CLEARDATAOUT 0x0090 #define OMAP24XX_GPIO_SETDATAOUT 0x0094 +/* + * omap34xx specific GPIO registers + */ + +#define OMAP34XX_GPIO1_BASE (void __iomem *)0x48310000 +#define OMAP34XX_GPIO2_BASE (void __iomem *)0x49050000 +#define OMAP34XX_GPIO3_BASE (void __iomem *)0x49052000 +#define OMAP34XX_GPIO4_BASE (void __iomem *)0x49054000 +#define OMAP34XX_GPIO5_BASE (void __iomem *)0x49056000 +#define OMAP34XX_GPIO6_BASE (void __iomem *)0x49058000 + + struct gpio_bank { void __iomem *base; u16 irq; u16 virtual_irq_start; int method; u32 reserved_map; -#if defined (CONFIG_ARCH_OMAP16XX) || defined (CONFIG_ARCH_OMAP24XX) +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) u32 suspend_wakeup; u32 saved_wakeup; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; @@ -192,48 +206,52 @@ static struct gpio_bank gpio_bank_243x[5] = { #endif +#ifdef CONFIG_ARCH_OMAP34XX +static struct gpio_bank gpio_bank_34xx[6] = { + { OMAP34XX_GPIO1_BASE, INT_34XX_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_24XX }, + { OMAP34XX_GPIO2_BASE, INT_34XX_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_24XX }, + { OMAP34XX_GPIO3_BASE, INT_34XX_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_24XX }, + { OMAP34XX_GPIO4_BASE, INT_34XX_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_24XX }, + { OMAP34XX_GPIO5_BASE, INT_34XX_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_24XX }, + { OMAP34XX_GPIO6_BASE, INT_34XX_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_24XX }, +}; + +#endif + static struct gpio_bank *gpio_bank; static int gpio_bank_count; static inline struct gpio_bank *get_gpio_bank(int gpio) { -#ifdef CONFIG_ARCH_OMAP15XX if (cpu_is_omap15xx()) { if (OMAP_GPIO_IS_MPUIO(gpio)) return &gpio_bank[0]; return &gpio_bank[1]; } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) if (cpu_is_omap16xx()) { if (OMAP_GPIO_IS_MPUIO(gpio)) return &gpio_bank[0]; return &gpio_bank[1 + (gpio >> 4)]; } -#endif -#ifdef CONFIG_ARCH_OMAP730 if (cpu_is_omap730()) { if (OMAP_GPIO_IS_MPUIO(gpio)) return &gpio_bank[0]; return &gpio_bank[1 + (gpio >> 5)]; } -#endif -#ifdef CONFIG_ARCH_OMAP24XX if (cpu_is_omap24xx()) return &gpio_bank[gpio >> 5]; -#endif + if (cpu_is_omap34xx()) + return &gpio_bank[gpio >> 5]; } static inline int get_gpio_index(int gpio) { -#ifdef CONFIG_ARCH_OMAP730 if (cpu_is_omap730()) return gpio & 0x1f; -#endif -#ifdef CONFIG_ARCH_OMAP24XX if (cpu_is_omap24xx()) return gpio & 0x1f; -#endif + if (cpu_is_omap34xx()) + return gpio & 0x1f; return gpio & 0x0f; } @@ -241,29 +259,21 @@ static inline int gpio_valid(int gpio) { if (gpio < 0) return -1; -#ifndef CONFIG_ARCH_OMAP24XX - if (OMAP_GPIO_IS_MPUIO(gpio)) { + if (cpu_class_is_omap1() && OMAP_GPIO_IS_MPUIO(gpio)) { if (gpio >= OMAP_MAX_GPIO_LINES + 16) return -1; return 0; } -#endif -#ifdef CONFIG_ARCH_OMAP15XX if (cpu_is_omap15xx() && gpio < 16) return 0; -#endif -#if defined(CONFIG_ARCH_OMAP16XX) if ((cpu_is_omap16xx()) && gpio < 64) return 0; -#endif -#ifdef CONFIG_ARCH_OMAP730 if (cpu_is_omap730() && gpio < 192) return 0; -#endif -#ifdef CONFIG_ARCH_OMAP24XX if (cpu_is_omap24xx() && gpio < 128) return 0; -#endif + if (cpu_is_omap34xx() && gpio < 160) + return 0; return -1; } @@ -303,7 +313,7 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) reg += OMAP730_GPIO_DIR_CONTROL; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: reg += OMAP24XX_GPIO_OE; break; @@ -377,7 +387,7 @@ static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) l &= ~(1 << gpio); break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: if (enable) reg += OMAP24XX_GPIO_SETDATAOUT; @@ -435,7 +445,7 @@ int omap_get_gpio_datain(int gpio) reg += OMAP730_GPIO_DATA_INPUT; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: reg += OMAP24XX_GPIO_DATAIN; break; @@ -455,8 +465,50 @@ do { \ __raw_writel(l, base + reg); \ } while(0) -#ifdef CONFIG_ARCH_OMAP24XX -static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) +void omap_set_gpio_debounce(int gpio, int enable) +{ + struct gpio_bank *bank; + void __iomem *reg; + u32 val, l = 1 << get_gpio_index(gpio); + + if (cpu_class_is_omap1()) + return; + + bank = get_gpio_bank(gpio); + reg = bank->base; + + reg += OMAP24XX_GPIO_DEBOUNCE_EN; + val = __raw_readl(reg); + + if (enable) + val |= l; + else + val &= ~l; + + __raw_writel(val, reg); +} +EXPORT_SYMBOL(omap_set_gpio_debounce); + +void omap_set_gpio_debounce_time(int gpio, int enc_time) +{ + struct gpio_bank *bank; + void __iomem *reg; + + if (cpu_class_is_omap1()) + return; + + bank = get_gpio_bank(gpio); + reg = bank->base; + + enc_time &= 0xff; + reg += OMAP24XX_GPIO_DEBOUNCE_VAL; + __raw_writel(enc_time, reg); +} +EXPORT_SYMBOL(omap_set_gpio_debounce_time); + +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) +static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, + int trigger) { void __iomem *base = bank->base; u32 gpio_bit = 1 << gpio; @@ -469,19 +521,25 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, in trigger & __IRQT_RISEDGE); MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, trigger & __IRQT_FALEDGE); + if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { if (trigger != 0) - __raw_writel(1 << gpio, bank->base + OMAP24XX_GPIO_SETWKUENA); + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_SETWKUENA); else - __raw_writel(1 << gpio, bank->base + OMAP24XX_GPIO_CLEARWKUENA); + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_CLEARWKUENA); } else { if (trigger != 0) bank->enabled_non_wakeup_gpios |= gpio_bit; else bank->enabled_non_wakeup_gpios &= ~gpio_bit; } - /* FIXME: Possibly do 'set_irq_handler(j, handle_level_irq)' if only level - * triggering requested. */ + + /* + * FIXME: Possibly do 'set_irq_handler(j, handle_level_irq)' if only + * level triggering requested. + */ } #endif @@ -547,7 +605,7 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) goto bad; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: set_24xx_gpio_triggering(bank, gpio, trigger); break; @@ -567,7 +625,7 @@ static int gpio_irq_type(unsigned irq, unsigned type) unsigned gpio; int retval; - if (!cpu_is_omap24xx() && irq > IH_MPUIO_BASE) + if (!cpu_class_is_omap2() && irq > IH_MPUIO_BASE) gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); else gpio = irq - IH_GPIO_BASE; @@ -579,7 +637,7 @@ static int gpio_irq_type(unsigned irq, unsigned type) return -EINVAL; /* OMAP1 allows only only edge triggering */ - if (!cpu_is_omap24xx() + if (!cpu_class_is_omap2() && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) return -EINVAL; @@ -620,7 +678,7 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) reg += OMAP730_GPIO_INT_STATUS; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: reg += OMAP24XX_GPIO_IRQSTATUS1; break; @@ -632,8 +690,10 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) __raw_writel(gpio_mask, reg); /* Workaround for clearing DSP GPIO interrupts to allow retention */ - if (cpu_is_omap2420()) +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) + if (cpu_is_omap24xx() || cpu_is_omap34xx()) __raw_writel(gpio_mask, bank->base + OMAP24XX_GPIO_IRQSTATUS2); +#endif } static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) @@ -676,7 +736,7 @@ static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank) inv = 1; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: reg += OMAP24XX_GPIO_IRQENABLE1; mask = 0xffffffff; @@ -739,7 +799,7 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enab l |= gpio_mask; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: if (enable) reg += OMAP24XX_GPIO_SETIRQENABLE1; @@ -785,7 +845,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) spin_unlock(&bank->lock); return 0; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: if (bank->non_wakeup_gpios & (1 << gpio)) { printk(KERN_ERR "Unable to modify wakeup on " @@ -891,7 +951,7 @@ void omap_free_gpio(int gpio) __raw_writel(1 << get_gpio_index(gpio), reg); } #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) if (bank->method == METHOD_GPIO_24XX) { /* Disable wake-up during idle for dynamic tick */ void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA; @@ -940,7 +1000,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) if (bank->method == METHOD_GPIO_730) isr_reg = bank->base + OMAP730_GPIO_INT_STATUS; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) if (bank->method == METHOD_GPIO_24XX) isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; #endif @@ -954,7 +1014,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) isr &= 0x0000ffff; - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { level_mask = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) | @@ -1023,7 +1083,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } } - if (cpu_is_omap24xx()) { + if (cpu_class_is_omap2()) { /* clear level sensitive interrupts after handler(s) */ _enable_gpio_irqbank(bank, isr_saved & level_mask, 0); _clear_gpio_irqbank(bank, isr_saved & level_mask); @@ -1199,21 +1259,35 @@ static inline void mpuio_init(void) {} /*---------------------------------------------------------------------*/ static int initialized; +#if !defined(CONFIG_ARCH_OMAP3) static struct clk * gpio_ick; +#endif + +#if defined(CONFIG_ARCH_OMAP2) static struct clk * gpio_fck; +#endif -#ifdef CONFIG_ARCH_OMAP2430 +#if defined(CONFIG_ARCH_OMAP2430) static struct clk * gpio5_ick; static struct clk * gpio5_fck; #endif +#if defined(CONFIG_ARCH_OMAP3) +static struct clk *gpio_fclks[OMAP34XX_NR_GPIOS]; +static struct clk *gpio_iclks[OMAP34XX_NR_GPIOS]; +#endif + static int __init _omap_gpio_init(void) { int i; struct gpio_bank *bank; +#if defined(CONFIG_ARCH_OMAP3) + char clk_name[11]; +#endif initialized = 1; +#if defined(CONFIG_ARCH_OMAP1) if (cpu_is_omap15xx()) { gpio_ick = clk_get(NULL, "arm_gpio_ck"); if (IS_ERR(gpio_ick)) @@ -1221,7 +1295,9 @@ static int __init _omap_gpio_init(void) else clk_enable(gpio_ick); } - if (cpu_is_omap24xx()) { +#endif +#if defined(CONFIG_ARCH_OMAP2) + if (cpu_class_is_omap2()) { gpio_ick = clk_get(NULL, "gpios_ick"); if (IS_ERR(gpio_ick)) printk("Could not get gpios_ick\n"); @@ -1234,9 +1310,9 @@ static int __init _omap_gpio_init(void) clk_enable(gpio_fck); /* - * On 2430 GPIO 5 uses CORE L4 ICLK + * On 2430 & 3430 GPIO 5 uses CORE L4 ICLK */ -#ifdef CONFIG_ARCH_OMAP2430 +#if defined(CONFIG_ARCH_OMAP2430) if (cpu_is_omap2430()) { gpio5_ick = clk_get(NULL, "gpio5_ick"); if (IS_ERR(gpio5_ick)) @@ -1250,7 +1326,28 @@ static int __init _omap_gpio_init(void) clk_enable(gpio5_fck); } #endif -} + } +#endif + +#if defined(CONFIG_ARCH_OMAP3) + if (cpu_is_omap34xx()) { + for (i = 0; i < OMAP34XX_NR_GPIOS; i++) { + sprintf(clk_name, "gpio%d_ick", i + 1); + gpio_iclks[i] = clk_get(NULL, clk_name); + if (IS_ERR(gpio_iclks[i])) + printk(KERN_ERR "Could not get %s\n", clk_name); + else + clk_enable(gpio_iclks[i]); + sprintf(clk_name, "gpio%d_fck", i + 1); + gpio_fclks[i] = clk_get(NULL, clk_name); + if (IS_ERR(gpio_fclks[i])) + printk(KERN_ERR "Could not get %s\n", clk_name); + else + clk_enable(gpio_fclks[i]); + } + } +#endif + #ifdef CONFIG_ARCH_OMAP15XX if (cpu_is_omap15xx()) { @@ -1298,6 +1395,17 @@ static int __init _omap_gpio_init(void) (rev >> 4) & 0x0f, rev & 0x0f); } #endif +#ifdef CONFIG_ARCH_OMAP34XX + if (cpu_is_omap34xx()) { + int rev; + + gpio_bank_count = OMAP34XX_NR_GPIOS; + gpio_bank = gpio_bank_34xx; + rev = omap_readl(gpio_bank[0].base + OMAP24XX_GPIO_REVISION); + printk(KERN_INFO "OMAP34xx GPIO hardware version %d.%d\n", + (rev >> 4) & 0x0f, rev & 0x0f); + } +#endif for (i = 0; i < gpio_bank_count; i++) { int j, gpio_count = 16; @@ -1307,28 +1415,23 @@ static int __init _omap_gpio_init(void) spin_lock_init(&bank->lock); if (bank_is_mpuio(bank)) omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT); -#ifdef CONFIG_ARCH_OMAP15XX - if (bank->method == METHOD_GPIO_1510) { + if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) { __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (bank->method == METHOD_GPIO_1610) { + if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) { __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); __raw_writew(0x0014, bank->base + OMAP1610_GPIO_SYSCONFIG); } -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (bank->method == METHOD_GPIO_730) { + if (cpu_is_omap730() && bank->method == METHOD_GPIO_730) { __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK); __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS); gpio_count = 32; /* 730 has 32-bit GPIOs */ } -#endif -#ifdef CONFIG_ARCH_OMAP24XX + +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) if (bank->method == METHOD_GPIO_24XX) { static const u32 non_wakeup_gpios[] = { 0xe203ffc0, 0x08700040 @@ -1364,21 +1467,21 @@ static int __init _omap_gpio_init(void) if (cpu_is_omap16xx()) omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); -#ifdef CONFIG_ARCH_OMAP24XX /* Enable autoidle for the OCP interface */ if (cpu_is_omap24xx()) omap_writel(1 << 0, 0x48019010); -#endif + if (cpu_is_omap34xx()) + omap_writel(1 << 0, 0x48306814); return 0; } -#if defined (CONFIG_ARCH_OMAP16XX) || defined (CONFIG_ARCH_OMAP24XX) +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) static int omap_gpio_suspend(struct sys_device *dev, pm_message_t mesg) { int i; - if (!cpu_is_omap24xx() && !cpu_is_omap16xx()) + if (!cpu_class_is_omap2() && !cpu_is_omap16xx()) return 0; for (i = 0; i < gpio_bank_count; i++) { @@ -1395,7 +1498,7 @@ static int omap_gpio_suspend(struct sys_device *dev, pm_message_t mesg) wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: wake_status = bank->base + OMAP24XX_GPIO_SETWKUENA; wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; @@ -1435,7 +1538,7 @@ static int omap_gpio_resume(struct sys_device *dev) wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; break; #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) case METHOD_GPIO_24XX: wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA; wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA; @@ -1467,7 +1570,7 @@ static struct sys_device omap_gpio_device = { #endif -#ifdef CONFIG_ARCH_OMAP24XX +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) static int workaround_enabled; @@ -1483,15 +1586,19 @@ void omap2_gpio_prepare_for_retention(void) if (!(bank->enabled_non_wakeup_gpios)) continue; +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) bank->saved_datain = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); l1 = __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT); l2 = __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT); +#endif bank->saved_fallingdetect = l1; bank->saved_risingdetect = l2; l1 &= ~bank->enabled_non_wakeup_gpios; l2 &= ~bank->enabled_non_wakeup_gpios; +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) __raw_writel(l1, bank->base + OMAP24XX_GPIO_FALLINGDETECT); __raw_writel(l2, bank->base + OMAP24XX_GPIO_RISINGDETECT); +#endif c++; } if (!c) { @@ -1513,26 +1620,31 @@ void omap2_gpio_resume_after_retention(void) if (!(bank->enabled_non_wakeup_gpios)) continue; +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) __raw_writel(bank->saved_fallingdetect, bank->base + OMAP24XX_GPIO_FALLINGDETECT); __raw_writel(bank->saved_risingdetect, bank->base + OMAP24XX_GPIO_RISINGDETECT); +#endif /* Check if any of the non-wakeup interrupt GPIOs have changed * state. If so, generate an IRQ by software. This is * horribly racy, but it's the best we can do to work around * this silicon bug. */ +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); +#endif l ^= bank->saved_datain; l &= bank->non_wakeup_gpios; if (l) { u32 old0, old1; - +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) old0 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0); old1 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); __raw_writel(old0 | l, bank->base + OMAP24XX_GPIO_LEVELDETECT0); __raw_writel(old1 | l, bank->base + OMAP24XX_GPIO_LEVELDETECT1); __raw_writel(old0, bank->base + OMAP24XX_GPIO_LEVELDETECT0); __raw_writel(old1, bank->base + OMAP24XX_GPIO_LEVELDETECT1); +#endif } } @@ -1561,8 +1673,8 @@ static int __init omap_gpio_sysinit(void) mpuio_init(); -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP24XX) - if (cpu_is_omap16xx() || cpu_is_omap24xx()) { +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) + if (cpu_is_omap16xx() || cpu_class_is_omap2()) { if (ret == 0) { ret = sysdev_class_register(&omap_gpio_sysclass); if (ret == 0) @@ -1624,7 +1736,7 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) if (bank_is_mpuio(bank)) gpio = OMAP_MPUIO(0); - else if (cpu_is_omap24xx() || cpu_is_omap730()) + else if (cpu_class_is_omap2() || cpu_is_omap730()) bankwidth = 32; for (j = 0; j < bankwidth; j++, gpio++, mask <<= 1) { diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c new file mode 100644 index 00000000000..7990ab185bb --- /dev/null +++ b/arch/arm/plat-omap/i2c.c @@ -0,0 +1,148 @@ +/* + * linux/arch/arm/plat-omap/i2c.c + * + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Jarkko Nikula <jarkko.nikula@nokia.com> + * + * 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. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <asm/mach-types.h> +#include <asm/arch/mux.h> + +#define OMAP_I2C_SIZE 0x3f +#define OMAP1_I2C_BASE 0xfffb3800 +#define OMAP2_I2C_BASE1 0x48070000 +#define OMAP2_I2C_BASE2 0x48072000 +#define OMAP2_I2C_BASE3 0x48060000 + +static const char name[] = "i2c_omap"; + +#define I2C_RESOURCE_BUILDER(base, irq) \ + { \ + .start = (base), \ + .end = (base) + OMAP_I2C_SIZE, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = (irq), \ + .flags = IORESOURCE_IRQ, \ + }, + +static struct resource i2c_resources[][2] = { + { I2C_RESOURCE_BUILDER(0, 0) }, +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) + { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE2, INT_24XX_I2C2_IRQ) }, +#endif +#if defined(CONFIG_ARCH_OMAP34XX) + { I2C_RESOURCE_BUILDER(OMAP2_I2C_BASE3, INT_34XX_I2C3_IRQ) }, +#endif +}; + +#define I2C_DEV_BUILDER(bus_id, res, data) \ + { \ + .id = (bus_id), \ + .name = name, \ + .num_resources = ARRAY_SIZE(res), \ + .resource = (res), \ + .dev = { \ + .platform_data = (data), \ + }, \ + } + +static u32 i2c_rate[ARRAY_SIZE(i2c_resources)]; +static struct platform_device omap_i2c_devices[] = { + I2C_DEV_BUILDER(1, i2c_resources[0], &i2c_rate[0]), +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) + I2C_DEV_BUILDER(2, i2c_resources[1], &i2c_rate[1]), +#endif +#if defined(CONFIG_ARCH_OMAP34XX) + I2C_DEV_BUILDER(3, i2c_resources[2], &i2c_rate[2]), +#endif +}; + +static void __init omap_i2c_mux_pins(int bus_id) +{ + /* TODO: Muxing for OMAP3 */ + switch (bus_id) { + case 1: + if (cpu_class_is_omap1()) { + omap_cfg_reg(I2C_SCL); + omap_cfg_reg(I2C_SDA); + } else if (cpu_is_omap24xx()) { + omap_cfg_reg(M19_24XX_I2C1_SCL); + omap_cfg_reg(L15_24XX_I2C1_SDA); + } + break; + case 2: + if (cpu_is_omap24xx()) { + omap_cfg_reg(J15_24XX_I2C2_SCL); + omap_cfg_reg(H19_24XX_I2C2_SDA); + } + break; + } +} + +int __init omap_register_i2c_bus(int bus_id, u32 clkrate, + struct i2c_board_info const *info, + unsigned len) +{ + int ports, err; + struct platform_device *pdev; + struct resource *res; + resource_size_t base, irq; + + if (cpu_class_is_omap1()) + ports = 1; + else if (cpu_is_omap24xx()) + ports = 2; + else if (cpu_is_omap34xx()) + ports = 3; + + BUG_ON(bus_id < 1 || bus_id > ports); + + if (info) { + err = i2c_register_board_info(bus_id, info, len); + if (err) + return err; + } + + pdev = &omap_i2c_devices[bus_id - 1]; + *(u32 *)pdev->dev.platform_data = clkrate; + + if (bus_id == 1) { + res = pdev->resource; + if (cpu_class_is_omap1()) { + base = OMAP1_I2C_BASE; + irq = INT_I2C; + } else { + base = OMAP2_I2C_BASE1; + irq = INT_24XX_I2C1_IRQ; + } + res[0].start = base; + res[0].end = base + OMAP_I2C_SIZE; + res[1].start = irq; + } + + omap_i2c_mux_pins(bus_id); + return platform_device_register(pdev); +} diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index 2af5bd5a134..9cf83c4da9f 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c @@ -201,6 +201,14 @@ static int omap_mcbsp_check(unsigned int id) static void omap_mcbsp_dsp_request(void) { if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + int ret; + + ret = omap_dsp_request_mem(); + if (ret < 0) { + printk(KERN_ERR "Could not get dsp memory: %i\n", ret); + return; + } + clk_enable(mcbsp_dsp_ck); clk_enable(mcbsp_api_ck); @@ -219,6 +227,7 @@ static void omap_mcbsp_dsp_request(void) static void omap_mcbsp_dsp_free(void) { if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + omap_dsp_release_mem(); clk_disable(mcbsp_dspxor_ck); clk_disable(mcbsp_dsp_ck); clk_disable(mcbsp_api_ck); @@ -1024,6 +1033,8 @@ EXPORT_SYMBOL(omap_mcbsp_set_io_type); EXPORT_SYMBOL(omap_mcbsp_free); EXPORT_SYMBOL(omap_mcbsp_start); EXPORT_SYMBOL(omap_mcbsp_stop); +EXPORT_SYMBOL(omap_mcbsp_pollread); +EXPORT_SYMBOL(omap_mcbsp_pollwrite); EXPORT_SYMBOL(omap_mcbsp_xmit_word); EXPORT_SYMBOL(omap_mcbsp_recv_word); EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); diff --git a/arch/avr32/kernel/ocd.c b/arch/avr32/kernel/ocd.c index c4f023294d7..1b0245d4e0c 100644 --- a/arch/avr32/kernel/ocd.c +++ b/arch/avr32/kernel/ocd.c @@ -90,25 +90,29 @@ static struct dentry *ocd_debugfs_DC; static struct dentry *ocd_debugfs_DS; static struct dentry *ocd_debugfs_count; -static u64 ocd_DC_get(void *data) +static int ocd_DC_get(void *data, u64 *val) { - return ocd_read(DC); + *val = ocd_read(DC); + return 0; } -static void ocd_DC_set(void *data, u64 val) +static int ocd_DC_set(void *data, u64 val) { ocd_write(DC, val); + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_DC, ocd_DC_get, ocd_DC_set, "0x%08llx\n"); -static u64 ocd_DS_get(void *data) +static int ocd_DS_get(void *data, u64 *val) { - return ocd_read(DS); + *val = ocd_read(DS); + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_DS, ocd_DS_get, NULL, "0x%08llx\n"); -static u64 ocd_count_get(void *data) +static int ocd_count_get(void *data, u64 *val) { - return ocd_count; + *val = ocd_count; + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_count, ocd_count_get, NULL, "%lld\n"); diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 14e61f05e1f..7678fee9a88 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1186,6 +1186,59 @@ err_dup_modedb: #endif /* -------------------------------------------------------------------- + * PWM + * -------------------------------------------------------------------- */ +static struct resource atmel_pwm0_resource[] __initdata = { + PBMEM(0xfff01400), + IRQ(24), +}; +static struct clk atmel_pwm0_mck = { + .name = "mck", + .parent = &pbb_clk, + .mode = pbb_clk_mode, + .get_rate = pbb_clk_get_rate, + .index = 5, +}; + +struct platform_device *__init at32_add_device_pwm(u32 mask) +{ + struct platform_device *pdev; + + if (!mask) + return NULL; + + pdev = platform_device_alloc("atmel_pwm", 0); + if (!pdev) + return NULL; + + if (platform_device_add_resources(pdev, atmel_pwm0_resource, + ARRAY_SIZE(atmel_pwm0_resource))) + goto out_free_pdev; + + if (platform_device_add_data(pdev, &mask, sizeof(mask))) + goto out_free_pdev; + + if (mask & (1 << 0)) + select_peripheral(PA(28), PERIPH_A, 0); + if (mask & (1 << 1)) + select_peripheral(PA(29), PERIPH_A, 0); + if (mask & (1 << 2)) + select_peripheral(PA(21), PERIPH_B, 0); + if (mask & (1 << 3)) + select_peripheral(PA(22), PERIPH_B, 0); + + atmel_pwm0_mck.dev = &pdev->dev; + + platform_device_add(pdev); + + return pdev; + +out_free_pdev: + platform_device_put(pdev); + return NULL; +} + +/* -------------------------------------------------------------------- * SSC * -------------------------------------------------------------------- */ static struct resource ssc0_resource[] = { @@ -1646,6 +1699,7 @@ struct clk *at32_clock_list[] = { &atmel_usart1_usart, &atmel_usart2_usart, &atmel_usart3_usart, + &atmel_pwm0_mck, #if defined(CONFIG_CPU_AT32AP7000) &macb0_hclk, &macb0_pclk, diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index ba21e33b8b1..368bc7fe167 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -544,7 +544,7 @@ config EXCPT_IRQ_SYSC_L1 default y help If enabled, the entire ASM lowlevel exception and interrupt entry code - (STORE/RESTORE CONTEXT) is linked into L1 instruction memory. + (STORE/RESTORE CONTEXT) is linked into L1 instruction memory. (less latency) config DO_IRQ_L1 @@ -904,29 +904,38 @@ config ARCH_SUSPEND_POSSIBLE depends on !SMP choice - prompt "Select PM Wakeup Event Source" - default PM_WAKEUP_GPIO_BY_SIC_IWR + prompt "Default Power Saving Mode" depends on PM - help - If you have a GPIO already configured as input with the corresponding PORTx_MASK - bit set - "Specify Wakeup Event by SIC_IWR value" + default PM_BFIN_SLEEP_DEEPER +config PM_BFIN_SLEEP_DEEPER + bool "Sleep Deeper" + help + Sleep "Deeper" Mode (High Power Savings) - This mode reduces dynamic + power dissipation by disabling the clock to the processor core (CCLK). + Furthermore, Standby sets the internal power supply voltage (VDDINT) + to 0.85 V to provide the greatest power savings, while preserving the + processor state. + The PLL and system clock (SCLK) continue to operate at a very low + frequency of about 3.3 MHz. To preserve data integrity in the SDRAM, + the SDRAM is put into Self Refresh Mode. Typically an external event + such as GPIO interrupt or RTC activity wakes up the processor. + Various Peripherals such as UART, SPORT, PPI may not function as + normal during Sleep Deeper, due to the reduced SCLK frequency. + When in the sleep mode, system DMA access to L1 memory is not supported. + +config PM_BFIN_SLEEP + bool "Sleep" + help + Sleep Mode (High Power Savings) - The sleep mode reduces power + dissipation by disabling the clock to the processor core (CCLK). + The PLL and system clock (SCLK), however, continue to operate in + this mode. Typically an external event or RTC activity will wake + up the processor. When in the sleep mode, + system DMA access to L1 memory is not supported. +endchoice -config PM_WAKEUP_GPIO_BY_SIC_IWR - bool "Specify Wakeup Event by SIC_IWR value" config PM_WAKEUP_BY_GPIO bool "Cause Wakeup Event by GPIO" -config PM_WAKEUP_GPIO_API - bool "Configure Wakeup Event by PM GPIO API" - -endchoice - -config PM_WAKEUP_SIC_IWR - hex "Wakeup Events (SIC_IWR)" - depends on PM_WAKEUP_GPIO_BY_SIC_IWR - default 0x8 if (BF537 || BF536 || BF534) - default 0x80 if (BF533 || BF532 || BF531) - default 0x80 if (BF54x) - default 0x80 if (BF52x) config PM_WAKEUP_GPIO_NUMBER int "Wakeup GPIO number" diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug index 59b87a483c6..c61bdebb997 100644 --- a/arch/blackfin/Kconfig.debug +++ b/arch/blackfin/Kconfig.debug @@ -164,7 +164,7 @@ config DUAL_CORE_TEST_MODULE config CPLB_INFO bool "Display the CPLB information" help - Display the CPLB information. + Display the CPLB information via /proc/cplbinfo. config ACCESS_CHECK bool "Check the user pointer address" diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile index 0edc402fef5..fe254f886a6 100644 --- a/arch/blackfin/Makefile +++ b/arch/blackfin/Makefile @@ -119,6 +119,7 @@ archclean: $(Q)$(MAKE) $(clean)=$(boot) +INSTALL_PATH ?= /tftpboot boot := arch/$(ARCH)/boot BOOT_TARGETS = vmImage PHONY += $(BOOT_TARGETS) install diff --git a/arch/blackfin/configs/BF533-STAMP_defconfig b/arch/blackfin/configs/BF533-STAMP_defconfig index 9b7123cf27a..198f4123af4 100644 --- a/arch/blackfin/configs/BF533-STAMP_defconfig +++ b/arch/blackfin/configs/BF533-STAMP_defconfig @@ -216,8 +216,6 @@ CONFIG_MEM_SIZE=128 CONFIG_MEM_ADD_WIDTH=11 CONFIG_ENET_FLASH_PIN=0 CONFIG_BOOT_LOAD=0x1000 - - CONFIG_BFIN_SCRATCH_REG_RETN=y # CONFIG_BFIN_SCRATCH_REG_RETE is not set # CONFIG_BFIN_SCRATCH_REG_CYCLES is not set @@ -483,7 +481,7 @@ CONFIG_MTD=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set -# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y # # User Modules And Translation Layers @@ -500,8 +498,8 @@ CONFIG_MTD_BLOCK=y # # RAM/ROM/Flash chip drivers # -# CONFIG_MTD_CFI is not set -CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_CFI=m +# CONFIG_MTD_JEDECPROBE is not set CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set CONFIG_MTD_MAP_BANK_WIDTH_1=y @@ -515,8 +513,9 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I4 is not set # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_CFI_INTELEXT is not set -# CONFIG_MTD_CFI_AMDSTD is not set +CONFIG_MTD_CFI_AMDSTD=m # CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=y CONFIG_MTD_ROM=m # CONFIG_MTD_ABSENT is not set @@ -526,6 +525,11 @@ CONFIG_MTD_ROM=m # CONFIG_MTD_COMPLEX_MAPPINGS=y # CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_BF5xx=m +CONFIG_BFIN_FLASH_BANK_0=0x7BB0 +CONFIG_BFIN_FLASH_BANK_1=0x7BB0 +CONFIG_BFIN_FLASH_BANK_2=0x7BB0 +CONFIG_BFIN_FLASH_BANK_3=0x7BB0 # CONFIG_MTD_UCLINUX is not set # CONFIG_MTD_PLATRAM is not set diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index fa9debe8d5f..5453bc3664f 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -104,6 +104,16 @@ int request_dma(unsigned int channel, char *device_id) mutex_unlock(&(dma_ch[channel].dmalock)); +#ifdef CONFIG_BF54x + if (channel >= CH_UART2_RX && channel <= CH_UART3_TX && + strncmp(device_id, "BFIN_UART", 9) == 0) + dma_ch[channel].regs->peripheral_map |= + (channel - CH_UART2_RX + 0xC); + else + dma_ch[channel].regs->peripheral_map |= + (channel - CH_UART2_RX + 0x6); +#endif + dma_ch[channel].device_id = device_id; dma_ch[channel].irq_callback = NULL; diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index 6bbe0a2fccb..08788f7bbfb 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -186,7 +186,7 @@ static struct str_ident { char name[RESOURCE_LABEL_SIZE]; } str_ident[MAX_RESOURCES]; -#ifdef CONFIG_PM +#if defined(CONFIG_PM) && !defined(CONFIG_BF54x) static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; static unsigned char wakeup_flags_map[MAX_BLACKFIN_GPIOS]; static struct gpio_port_s gpio_bank_saved[gpio_bank(MAX_BLACKFIN_GPIOS)]; @@ -696,9 +696,8 @@ static int bfin_gpio_wakeup_type(unsigned gpio, unsigned char type) return 0; } -u32 gpio_pm_setup(void) +u32 bfin_pm_setup(void) { - u32 sic_iwr = 0; u16 bank, mask, i, gpio; for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { @@ -723,7 +722,8 @@ u32 gpio_pm_setup(void) gpio = i; while (mask) { - if (mask & 1) { + if ((mask & 1) && (wakeup_flags_map[gpio] != + PM_WAKE_IGNORE)) { reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); bfin_gpio_wakeup_type(gpio, @@ -734,21 +734,17 @@ u32 gpio_pm_setup(void) mask >>= 1; } - sic_iwr |= 1 << - (sic_iwr_irqs[bank] - (IRQ_CORETMR + 1)); + bfin_internal_set_wake(sic_iwr_irqs[bank], 1); gpio_bankb[bank]->maskb_set = wakeup_map[gpio_bank(i)]; } } AWA_DUMMY_READ(maskb_set); - if (sic_iwr) - return sic_iwr; - else - return IWR_ENABLE_ALL; + return 0; } -void gpio_pm_restore(void) +void bfin_pm_restore(void) { u16 bank, mask, i; @@ -768,7 +764,7 @@ void gpio_pm_restore(void) reserved_gpio_map[bank] = gpio_bank_saved[bank].reserved; - + bfin_internal_set_wake(sic_iwr_irqs[bank], 0); } gpio_bankb[bank]->maskb = gpio_bank_saved[bank].maskb; diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c index e2e2b5079f5..dc6e8a7a8bd 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c @@ -26,6 +26,10 @@ #include <asm/cplb.h> #include <asm/cplbinit.h> +#if ANOMALY_05000263 +# error the MPU will not function safely while Anomaly 05000263 applies +#endif + struct cplb_entry icplb_tbl[MAX_CPLBS]; struct cplb_entry dcplb_tbl[MAX_CPLBS]; diff --git a/arch/blackfin/kernel/init_task.c b/arch/blackfin/kernel/init_task.c index 673c860ffc2..c640154030e 100644 --- a/arch/blackfin/kernel/init_task.c +++ b/arch/blackfin/kernel/init_task.c @@ -57,5 +57,5 @@ EXPORT_SYMBOL(init_task); * "init_task" linker map entry. */ union thread_union init_thread_union - __attribute__ ((__section__(".data.init_task"))) = { + __attribute__ ((__section__(".init_task.data"))) = { INIT_THREAD_INFO(init_task)}; diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 023dc80af18..6b8459c6616 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -31,7 +31,6 @@ #include <linux/smp_lock.h> #include <linux/unistd.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/uaccess.h> #include <linux/fs.h> #include <linux/err.h> diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 6e106b3d772..8229b1090eb 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -1,30 +1,11 @@ /* - * File: arch/blackfin/kernel/setup.c - * Based on: - * Author: + * arch/blackfin/kernel/setup.c * - * Created: - * Description: + * Copyright 2004-2006 Analog Devices Inc. * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. + * Enter bugs at http://blackfin.uclinux.org/ * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ #include <linux/delay.h> @@ -34,6 +15,7 @@ #include <linux/cpu.h> #include <linux/module.h> #include <linux/tty.h> +#include <linux/pfn.h> #include <linux/ext2_fs.h> #include <linux/cramfs_fs.h> @@ -47,6 +29,8 @@ #include <asm/fixed_code.h> #include <asm/early_printk.h> +static DEFINE_PER_CPU(struct cpu, cpu_devices); + u16 _bfin_swrst; unsigned long memory_start, memory_end, physical_mem_end; @@ -67,6 +51,29 @@ EXPORT_SYMBOL(mtd_size); char __initdata command_line[COMMAND_LINE_SIZE]; +/* boot memmap, for parsing "memmap=" */ +#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */ +#define BFIN_MEMMAP_RAM 1 +#define BFIN_MEMMAP_RESERVED 2 +struct bfin_memmap { + int nr_map; + struct bfin_memmap_entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; + unsigned long type; + } map[BFIN_MEMMAP_MAX]; +} bfin_memmap __initdata; + +/* for memmap sanitization */ +struct change_member { + struct bfin_memmap_entry *pentry; /* pointer to original entry */ + unsigned long long addr; /* address for this change point */ +}; +static struct change_member change_point_list[2*BFIN_MEMMAP_MAX] __initdata; +static struct change_member *change_point[2*BFIN_MEMMAP_MAX] __initdata; +static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata; +static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata; + void __init bf53x_cache_init(void) { #if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) @@ -123,12 +130,224 @@ void __init bf53x_relocate_l1_mem(void) } +/* add_memory_region to memmap */ +static void __init add_memory_region(unsigned long long start, + unsigned long long size, int type) +{ + int i; + + i = bfin_memmap.nr_map; + + if (i == BFIN_MEMMAP_MAX) { + printk(KERN_ERR "Ooops! Too many entries in the memory map!\n"); + return; + } + + bfin_memmap.map[i].addr = start; + bfin_memmap.map[i].size = size; + bfin_memmap.map[i].type = type; + bfin_memmap.nr_map++; +} + +/* + * Sanitize the boot memmap, removing overlaps. + */ +static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map) +{ + struct change_member *change_tmp; + unsigned long current_type, last_type; + unsigned long long last_addr; + int chgidx, still_changing; + int overlap_entries; + int new_entry; + int old_nr, new_nr, chg_nr; + int i; + + /* + Visually we're performing the following (1,2,3,4 = memory types) + + Sample memory map (w/overlaps): + ____22__________________ + ______________________4_ + ____1111________________ + _44_____________________ + 11111111________________ + ____________________33__ + ___________44___________ + __________33333_________ + ______________22________ + ___________________2222_ + _________111111111______ + _____________________11_ + _________________4______ + + Sanitized equivalent (no overlap): + 1_______________________ + _44_____________________ + ___1____________________ + ____22__________________ + ______11________________ + _________1______________ + __________3_____________ + ___________44___________ + _____________33_________ + _______________2________ + ________________1_______ + _________________4______ + ___________________2____ + ____________________33__ + ______________________4_ + */ + /* if there's only one memory region, don't bother */ + if (*pnr_map < 2) + return -1; + + old_nr = *pnr_map; + + /* bail out if we find any unreasonable addresses in memmap */ + for (i = 0; i < old_nr; i++) + if (map[i].addr + map[i].size < map[i].addr) + return -1; + + /* create pointers for initial change-point information (for sorting) */ + for (i = 0; i < 2*old_nr; i++) + change_point[i] = &change_point_list[i]; + + /* record all known change-points (starting and ending addresses), + omitting those that are for empty memory regions */ + chgidx = 0; + for (i = 0; i < old_nr; i++) { + if (map[i].size != 0) { + change_point[chgidx]->addr = map[i].addr; + change_point[chgidx++]->pentry = &map[i]; + change_point[chgidx]->addr = map[i].addr + map[i].size; + change_point[chgidx++]->pentry = &map[i]; + } + } + chg_nr = chgidx; /* true number of change-points */ + + /* sort change-point list by memory addresses (low -> high) */ + still_changing = 1; + while (still_changing) { + still_changing = 0; + for (i = 1; i < chg_nr; i++) { + /* if <current_addr> > <last_addr>, swap */ + /* or, if current=<start_addr> & last=<end_addr>, swap */ + if ((change_point[i]->addr < change_point[i-1]->addr) || + ((change_point[i]->addr == change_point[i-1]->addr) && + (change_point[i]->addr == change_point[i]->pentry->addr) && + (change_point[i-1]->addr != change_point[i-1]->pentry->addr)) + ) { + change_tmp = change_point[i]; + change_point[i] = change_point[i-1]; + change_point[i-1] = change_tmp; + still_changing = 1; + } + } + } + + /* create a new memmap, removing overlaps */ + overlap_entries = 0; /* number of entries in the overlap table */ + new_entry = 0; /* index for creating new memmap entries */ + last_type = 0; /* start with undefined memory type */ + last_addr = 0; /* start with 0 as last starting address */ + /* loop through change-points, determining affect on the new memmap */ + for (chgidx = 0; chgidx < chg_nr; chgidx++) { + /* keep track of all overlapping memmap entries */ + if (change_point[chgidx]->addr == change_point[chgidx]->pentry->addr) { + /* add map entry to overlap list (> 1 entry implies an overlap) */ + overlap_list[overlap_entries++] = change_point[chgidx]->pentry; + } else { + /* remove entry from list (order independent, so swap with last) */ + for (i = 0; i < overlap_entries; i++) { + if (overlap_list[i] == change_point[chgidx]->pentry) + overlap_list[i] = overlap_list[overlap_entries-1]; + } + overlap_entries--; + } + /* if there are overlapping entries, decide which "type" to use */ + /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */ + current_type = 0; + for (i = 0; i < overlap_entries; i++) + if (overlap_list[i]->type > current_type) + current_type = overlap_list[i]->type; + /* continue building up new memmap based on this information */ + if (current_type != last_type) { + if (last_type != 0) { + new_map[new_entry].size = + change_point[chgidx]->addr - last_addr; + /* move forward only if the new size was non-zero */ + if (new_map[new_entry].size != 0) + if (++new_entry >= BFIN_MEMMAP_MAX) + break; /* no more space left for new entries */ + } + if (current_type != 0) { + new_map[new_entry].addr = change_point[chgidx]->addr; + new_map[new_entry].type = current_type; + last_addr = change_point[chgidx]->addr; + } + last_type = current_type; + } + } + new_nr = new_entry; /* retain count for new entries */ + + /* copy new mapping into original location */ + memcpy(map, new_map, new_nr*sizeof(struct bfin_memmap_entry)); + *pnr_map = new_nr; + + return 0; +} + +static void __init print_memory_map(char *who) +{ + int i; + + for (i = 0; i < bfin_memmap.nr_map; i++) { + printk(KERN_DEBUG " %s: %016Lx - %016Lx ", who, + bfin_memmap.map[i].addr, + bfin_memmap.map[i].addr + bfin_memmap.map[i].size); + switch (bfin_memmap.map[i].type) { + case BFIN_MEMMAP_RAM: + printk("(usable)\n"); + break; + case BFIN_MEMMAP_RESERVED: + printk("(reserved)\n"); + break; + default: printk("type %lu\n", bfin_memmap.map[i].type); + break; + } + } +} + +static __init int parse_memmap(char *arg) +{ + unsigned long long start_at, mem_size; + + if (!arg) + return -EINVAL; + + mem_size = memparse(arg, &arg); + if (*arg == '@') { + start_at = memparse(arg+1, &arg); + add_memory_region(start_at, mem_size, BFIN_MEMMAP_RAM); + } else if (*arg == '$') { + start_at = memparse(arg+1, &arg); + add_memory_region(start_at, mem_size, BFIN_MEMMAP_RESERVED); + } + + return 0; +} + /* * Initial parsing of the command line. Currently, we support: * - Controlling the linux memory size: mem=xxx[KMG] * - Controlling the physical memory size: max_mem=xxx[KMG][$][#] * $ -> reserved memory is dcacheable * # -> reserved memory is icacheable + * - "memmap=XXX[KkmM][@][$]XXX[KkmM]" defines a memory region + * @ from <start> to <start>+<mem>, type RAM + * $ from <start> to <start>+<mem>, type RESERVED + * */ static __init void parse_cmdline_early(char *cmdline_p) { @@ -136,7 +355,6 @@ static __init void parse_cmdline_early(char *cmdline_p) unsigned int memsize; for (;;) { if (c == ' ') { - if (!memcmp(to, "mem=", 4)) { to += 4; memsize = memparse(to, &to); @@ -162,6 +380,9 @@ static __init void parse_cmdline_early(char *cmdline_p) } else if (!memcmp(to, "earlyprintk=", 12)) { to += 12; setup_early_printk(to); + } else if (!memcmp(to, "memmap=", 7)) { + to += 7; + parse_memmap(to); } } c = *(to++); @@ -170,75 +391,36 @@ static __init void parse_cmdline_early(char *cmdline_p) } } -void __init setup_arch(char **cmdline_p) +/* + * Setup memory defaults from user config. + * The physical memory layout looks like: + * + * [_rambase, _ramstart]: kernel image + * [memory_start, memory_end]: dynamic memory managed by kernel + * [memory_end, _ramend]: reserved memory + * [meory_mtd_start(memory_end), + * memory_mtd_start + mtd_size]: rootfs (if any) + * [_ramend - DMA_UNCACHED_REGION, + * _ramend]: uncached DMA region + * [_ramend, physical_mem_end]: memory not managed by kernel + * + */ +static __init void memory_setup(void) { - int bootmap_size; - unsigned long l1_length, sclk, cclk; #ifdef CONFIG_MTD_UCLINUX unsigned long mtd_phys = 0; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - -#if defined(CONFIG_CMDLINE_BOOL) - strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line)); - command_line[sizeof(command_line) - 1] = 0; -#endif - - /* Keep a copy of command line */ - *cmdline_p = &command_line[0]; - memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; - - /* setup memory defaults from the user config */ - physical_mem_end = 0; - _ramend = CONFIG_MEM_SIZE * 1024 * 1024; - - parse_cmdline_early(&command_line[0]); - - cclk = get_cclk(); - sclk = get_sclk(); - -#if !defined(CONFIG_BFIN_KERNEL_CLOCK) - if (ANOMALY_05000273 && cclk == sclk) - panic("ANOMALY 05000273, SCLK can not be same as CCLK"); -#endif + _rambase = (unsigned long)_stext; + _ramstart = (unsigned long)_end; -#ifdef BF561_FAMILY - if (ANOMALY_05000266) { - bfin_read_IMDMA_D0_IRQ_STATUS(); - bfin_read_IMDMA_D1_IRQ_STATUS(); + if (DMA_UNCACHED_REGION > (_ramend - _ramstart)) { + console_init(); + panic("DMA region exceeds memory limit: %lu.\n", + _ramend - _ramstart); } -#endif - - printk(KERN_INFO "Hardware Trace "); - if (bfin_read_TBUFCTL() & 0x1 ) - printk("Active "); - else - printk("Off "); - if (bfin_read_TBUFCTL() & 0x2) - printk("and Enabled\n"); - else - printk("and Disabled\n"); - - -#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH) - /* we need to initialize the Flashrom device here since we might - * do things with flash early on in the boot - */ - flash_probe(); -#endif - - if (physical_mem_end == 0) - physical_mem_end = _ramend; - - /* by now the stack is part of the init task */ memory_end = _ramend - DMA_UNCACHED_REGION; - _ramstart = (unsigned long)__bss_stop; - _rambase = (unsigned long)_stext; #ifdef CONFIG_MPU /* Round up to multiple of 4MB. */ memory_start = (_ramstart + 0x3fffff) & ~0x3fffff; @@ -292,7 +474,7 @@ void __init setup_arch(char **cmdline_p) } /* Relocate MTD image to the top of memory after the uncached memory area */ - dma_memcpy((char *)memory_end, __bss_stop, mtd_size); + dma_memcpy((char *)memory_end, _end, mtd_size); memory_mtd_start = memory_end; _ebss = memory_mtd_start; /* define _ebss for compatible */ @@ -319,13 +501,175 @@ void __init setup_arch(char **cmdline_p) #endif #if !defined(CONFIG_MTD_UCLINUX) - memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ + /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ + memory_end -= SIZE_4K; #endif + init_mm.start_code = (unsigned long)_stext; init_mm.end_code = (unsigned long)_etext; init_mm.end_data = (unsigned long)_edata; init_mm.brk = (unsigned long)0; + printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20); + printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20); + + printk(KERN_INFO "Memory map:\n" + KERN_INFO " text = 0x%p-0x%p\n" + KERN_INFO " rodata = 0x%p-0x%p\n" + KERN_INFO " bss = 0x%p-0x%p\n" + KERN_INFO " data = 0x%p-0x%p\n" + KERN_INFO " stack = 0x%p-0x%p\n" + KERN_INFO " init = 0x%p-0x%p\n" + KERN_INFO " available = 0x%p-0x%p\n" +#ifdef CONFIG_MTD_UCLINUX + KERN_INFO " rootfs = 0x%p-0x%p\n" +#endif +#if DMA_UNCACHED_REGION > 0 + KERN_INFO " DMA Zone = 0x%p-0x%p\n" +#endif + , _stext, _etext, + __start_rodata, __end_rodata, + __bss_start, __bss_stop, + _sdata, _edata, + (void *)&init_thread_union, + (void *)((int)(&init_thread_union) + 0x2000), + __init_begin, __init_end, + (void *)_ramstart, (void *)memory_end +#ifdef CONFIG_MTD_UCLINUX + , (void *)memory_mtd_start, (void *)(memory_mtd_start + mtd_size) +#endif +#if DMA_UNCACHED_REGION > 0 + , (void *)(_ramend - DMA_UNCACHED_REGION), (void *)(_ramend) +#endif + ); +} + +static __init void setup_bootmem_allocator(void) +{ + int bootmap_size; + int i; + unsigned long min_pfn, max_pfn; + unsigned long curr_pfn, last_pfn, size; + + /* mark memory between memory_start and memory_end usable */ + add_memory_region(memory_start, + memory_end - memory_start, BFIN_MEMMAP_RAM); + /* sanity check for overlap */ + sanitize_memmap(bfin_memmap.map, &bfin_memmap.nr_map); + print_memory_map("boot memmap"); + + min_pfn = PAGE_OFFSET >> PAGE_SHIFT; + max_pfn = memory_end >> PAGE_SHIFT; + + /* + * give all the memory to the bootmap allocator, tell it to put the + * boot mem_map at the start of memory. + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), + memory_start >> PAGE_SHIFT, /* map goes here */ + min_pfn, max_pfn); + + /* register the memmap regions with the bootmem allocator */ + for (i = 0; i < bfin_memmap.nr_map; i++) { + /* + * Reserve usable memory + */ + if (bfin_memmap.map[i].type != BFIN_MEMMAP_RAM) + continue; + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(bfin_memmap.map[i].addr); + if (curr_pfn >= max_pfn) + continue; + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(bfin_memmap.map[i].addr + + bfin_memmap.map[i].size); + + if (last_pfn > max_pfn) + last_pfn = max_pfn; + + /* + * .. finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + size = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + + /* reserve memory before memory_start, including bootmap */ + reserve_bootmem(PAGE_OFFSET, + memory_start + bootmap_size + PAGE_SIZE - 1 - PAGE_OFFSET, + BOOTMEM_DEFAULT); +} + +void __init setup_arch(char **cmdline_p) +{ + unsigned long l1_length, sclk, cclk; + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + +#if defined(CONFIG_CMDLINE_BOOL) + strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line)); + command_line[sizeof(command_line) - 1] = 0; +#endif + + /* Keep a copy of command line */ + *cmdline_p = &command_line[0]; + memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); + boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; + + /* setup memory defaults from the user config */ + physical_mem_end = 0; + _ramend = CONFIG_MEM_SIZE * 1024 * 1024; + + memset(&bfin_memmap, 0, sizeof(bfin_memmap)); + + parse_cmdline_early(&command_line[0]); + + if (physical_mem_end == 0) + physical_mem_end = _ramend; + + memory_setup(); + + cclk = get_cclk(); + sclk = get_sclk(); + +#if !defined(CONFIG_BFIN_KERNEL_CLOCK) + if (ANOMALY_05000273 && cclk == sclk) + panic("ANOMALY 05000273, SCLK can not be same as CCLK"); +#endif + +#ifdef BF561_FAMILY + if (ANOMALY_05000266) { + bfin_read_IMDMA_D0_IRQ_STATUS(); + bfin_read_IMDMA_D1_IRQ_STATUS(); + } +#endif + printk(KERN_INFO "Hardware Trace "); + if (bfin_read_TBUFCTL() & 0x1) + printk("Active "); + else + printk("Off "); + if (bfin_read_TBUFCTL() & 0x2) + printk("and Enabled\n"); + else + printk("and Disabled\n"); + +#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH) + /* we need to initialize the Flashrom device here since we might + * do things with flash early on in the boot + */ + flash_probe(); +#endif + _bfin_swrst = bfin_read_SWRST(); if (_bfin_swrst & RESET_DOUBLE) @@ -335,7 +679,7 @@ void __init setup_arch(char **cmdline_p) else if (_bfin_swrst & RESET_SOFTWARE) printk(KERN_NOTICE "Reset caused by Software reset\n"); - printk(KERN_INFO "Blackfin support (C) 2004-2007 Analog Devices, Inc.\n"); + printk(KERN_INFO "Blackfin support (C) 2004-2008 Analog Devices, Inc.\n"); if (bfin_compiled_revid() == 0xffff) printk(KERN_INFO "Compiled for ADSP-%s Rev any\n", CPU); else if (bfin_compiled_revid() == -1) @@ -361,55 +705,8 @@ void __init setup_arch(char **cmdline_p) if (ANOMALY_05000273 && (cclk >> 1) <= sclk) printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n"); - printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20); - printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20); - - printk(KERN_INFO "Memory map:\n" - KERN_INFO " text = 0x%p-0x%p\n" - KERN_INFO " rodata = 0x%p-0x%p\n" - KERN_INFO " data = 0x%p-0x%p\n" - KERN_INFO " stack = 0x%p-0x%p\n" - KERN_INFO " init = 0x%p-0x%p\n" - KERN_INFO " bss = 0x%p-0x%p\n" - KERN_INFO " available = 0x%p-0x%p\n" -#ifdef CONFIG_MTD_UCLINUX - KERN_INFO " rootfs = 0x%p-0x%p\n" -#endif -#if DMA_UNCACHED_REGION > 0 - KERN_INFO " DMA Zone = 0x%p-0x%p\n" -#endif - , _stext, _etext, - __start_rodata, __end_rodata, - _sdata, _edata, - (void *)&init_thread_union, (void *)((int)(&init_thread_union) + 0x2000), - __init_begin, __init_end, - __bss_start, __bss_stop, - (void *)_ramstart, (void *)memory_end -#ifdef CONFIG_MTD_UCLINUX - , (void *)memory_mtd_start, (void *)(memory_mtd_start + mtd_size) -#endif -#if DMA_UNCACHED_REGION > 0 - , (void *)(_ramend - DMA_UNCACHED_REGION), (void *)(_ramend) -#endif - ); + setup_bootmem_allocator(); - /* - * give all the memory to the bootmap allocator, tell it to put the - * boot mem_map at the start of memory - */ - bootmap_size = init_bootmem_node(NODE_DATA(0), memory_start >> PAGE_SHIFT, /* map goes here */ - PAGE_OFFSET >> PAGE_SHIFT, - memory_end >> PAGE_SHIFT); - /* - * free the usable memory, we have to make sure we do not free - * the bootmem bitmap so we then reserve it after freeing it :-) - */ - free_bootmem(memory_start, memory_end - memory_start); - - reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); - /* - * get kmalloc into gear - */ paging_init(); /* check the size of the l1 area */ @@ -450,15 +747,15 @@ void __init setup_arch(char **cmdline_p) static int __init topology_init(void) { -#if defined (CONFIG_BF561) - static struct cpu cpu[2]; - register_cpu(&cpu[0], 0); - register_cpu(&cpu[1], 1); + int cpu; + + for_each_possible_cpu(cpu) { + struct cpu *c = &per_cpu(cpu_devices, cpu); + + register_cpu(c, cpu); + } + return 0; -#else - static struct cpu cpu[1]; - return register_cpu(cpu, 0); -#endif } subsys_initcall(topology_init); @@ -700,7 +997,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 66b5f3e3ae2..58717cb1970 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -649,7 +649,7 @@ void dump_bfin_process(struct pt_regs *fp) if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) printk(KERN_NOTICE "HW Error context\n"); else if (context & 0x0020) - printk(KERN_NOTICE "Defered Exception context\n"); + printk(KERN_NOTICE "Deferred Exception context\n"); else if (context & 0x3FC0) printk(KERN_NOTICE "Interrupt context\n"); else if (context & 0x4000) diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S index 858722421b4..aed832540b3 100644 --- a/arch/blackfin/kernel/vmlinux.lds.S +++ b/arch/blackfin/kernel/vmlinux.lds.S @@ -41,6 +41,9 @@ _jiffies = _jiffies_64; SECTIONS { . = CONFIG_BOOT_LOAD; + /* Neither the text, ro_data or bss section need to be aligned + * So pack them back to back + */ .text : { __text = .; @@ -58,22 +61,25 @@ SECTIONS *(__ex_table) ___stop___ex_table = .; - . = ALIGN(4); __etext = .; } - RO_DATA(PAGE_SIZE) + /* Just in case the first read only is a 32-bit access */ + RO_DATA(4) + + .bss : + { + . = ALIGN(4); + ___bss_start = .; + *(.bss .bss.*) + *(COMMON) + ___bss_stop = .; + } .data : { - /* make sure the init_task is aligned to the - * kernel thread size so we can locate the kernel - * stack properly and quickly. - */ __sdata = .; - . = ALIGN(THREAD_SIZE); - *(.data.init_task) - + /* This gets done first, so the glob doesn't suck it in */ . = ALIGN(32); *(.data.cacheline_aligned) @@ -81,10 +87,22 @@ SECTIONS *(.data.*) CONSTRUCTORS + /* make sure the init_task is aligned to the + * kernel thread size so we can locate the kernel + * stack properly and quickly. + */ . = ALIGN(THREAD_SIZE); + *(.init_task.data) + __edata = .; } + /* The init section should be last, so when we free it, it goes into + * the general memory pool, and (hopefully) will decrease fragmentation + * a tiny bit. The init section has a _requirement_ that it be + * PAGE_SIZE aligned + */ + . = ALIGN(PAGE_SIZE); ___init_begin = .; .init.text : @@ -179,16 +197,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); ___init_end = .; - .bss : - { - . = ALIGN(4); - ___bss_start = .; - *(.bss .bss.*) - *(COMMON) - . = ALIGN(4); - ___bss_stop = .; - __end = .; - } + __end =.; STABS_DEBUG diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 1795aab7906..337515fba61 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -41,7 +41,9 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/usb/sl811.h> +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> +#endif #include <asm/cplb.h> #include <asm/dma.h> #include <asm/bfin5xx_spi.h> @@ -517,6 +519,14 @@ static struct bfin5xx_spi_chip spi_wm8731_chip_info = { .bits_per_word = 16, }; #endif + +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -634,6 +644,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .mode = SPI_MODE_0, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI controller data */ diff --git a/arch/blackfin/mach-bf533/boards/ezkit.c b/arch/blackfin/mach-bf533/boards/ezkit.c index 4026c2f3ab4..2b09aa39f56 100644 --- a/arch/blackfin/mach-bf533/boards/ezkit.c +++ b/arch/blackfin/mach-bf533/boards/ezkit.c @@ -34,7 +34,9 @@ #include <linux/mtd/partitions.h> #include <linux/spi/spi.h> #include <linux/spi/flash.h> +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) #include <linux/usb/isp1362.h> +#endif #include <linux/ata_platform.h> #include <linux/irq.h> #include <asm/dma.h> @@ -134,6 +136,13 @@ static struct bfin5xx_spi_chip ad1836_spi_chip_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) { @@ -168,6 +177,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &ad1836_spi_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c index 0185350feac..a645f6fd091 100644 --- a/arch/blackfin/mach-bf533/boards/stamp.c +++ b/arch/blackfin/mach-bf533/boards/stamp.c @@ -226,6 +226,13 @@ static struct bfin5xx_spi_chip spi_mmc_chip_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) { @@ -312,6 +319,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .mode = SPI_MODE_2, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ @@ -423,9 +439,9 @@ static struct platform_device bfin_pata_device = { #include <linux/gpio_keys.h> static struct gpio_keys_button bfin_gpio_keys_table[] = { - {BTN_0, GPIO_PF5, 1, "gpio-keys: BTN0"}, - {BTN_1, GPIO_PF6, 1, "gpio-keys: BTN1"}, - {BTN_2, GPIO_PF8, 1, "gpio-keys: BTN2"}, + {BTN_0, GPIO_PF5, 0, "gpio-keys: BTN0"}, + {BTN_1, GPIO_PF6, 0, "gpio-keys: BTN1"}, + {BTN_2, GPIO_PF8, 0, "gpio-keys: BTN2"}, }; static struct gpio_keys_platform_data bfin_gpio_keys_data = { diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index 119e6ea8338..9e2277e0d25 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -487,6 +487,13 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -593,6 +600,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &spi_ad7877_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI controller data */ diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index 14860f04d1b..916e963e83b 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -37,7 +37,9 @@ #include <linux/spi/flash.h> #include <linux/irq.h> #include <linux/interrupt.h> +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> +#endif #include <asm/bfin5xx_spi.h> #include <asm/cplb.h> #include <asm/dma.h> @@ -420,6 +422,13 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bf54x_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -445,6 +454,15 @@ static struct spi_board_info bf54x_spi_board_info[] __initdata = { .controller_data = &spi_ad7877_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ @@ -631,7 +649,7 @@ static struct platform_device *ezkit_devices[] __initdata = { &ezkit_flash_device, }; -static int __init stamp_init(void) +static int __init ezkit_init(void) { printk(KERN_INFO "%s(): registering device resources\n", __FUNCTION__); platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices)); @@ -644,4 +662,4 @@ static int __init stamp_init(void) return 0; } -arch_initcall(stamp_init); +arch_initcall(ezkit_init); diff --git a/arch/blackfin/mach-bf548/dma.c b/arch/blackfin/mach-bf548/dma.c index 957bf1366ef..374803a8d2e 100644 --- a/arch/blackfin/mach-bf548/dma.c +++ b/arch/blackfin/mach-bf548/dma.c @@ -1,5 +1,5 @@ /* - * File: arch/blackfin/mach-bf561/dma.c + * File: arch/blackfin/mach-bf548/dma.c * Based on: * Author: * @@ -7,7 +7,7 @@ * Description: This file contains the simple DMA Implementation for Blackfin * * Modified: - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c index ed863ce9a2d..43c1b098281 100644 --- a/arch/blackfin/mach-bf561/boards/ezkit.c +++ b/arch/blackfin/mach-bf561/boards/ezkit.c @@ -92,6 +92,68 @@ void __exit bfin_isp1761_exit(void) arch_initcall(bfin_isp1761_init); #endif +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) +#include <linux/usb/isp1362.h> + +static struct resource isp1362_hcd_resources[] = { + { + .start = 0x2c060000, + .end = 0x2c060000, + .flags = IORESOURCE_MEM, + }, { + .start = 0x2c060004, + .end = 0x2c060004, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_PF8, + .end = IRQ_PF8, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct isp1362_platform_data isp1362_priv = { + .sel15Kres = 1, + .clknotstop = 0, + .oc_enable = 0, + .int_act_high = 0, + .int_edge_triggered = 0, + .remote_wakeup_connected = 0, + .no_power_switching = 1, + .power_switching_mode = 0, +}; + +static struct platform_device isp1362_hcd_device = { + .name = "isp1362-hcd", + .id = 0, + .dev = { + .platform_data = &isp1362_priv, + }, + .num_resources = ARRAY_SIZE(isp1362_hcd_resources), + .resource = isp1362_hcd_resources, +}; +#endif + +#if defined(CONFIG_USB_NET2272) || defined(CONFIG_USB_NET2272_MODULE) +static struct resource net2272_bfin_resources[] = { + { + .start = 0x2C000000, + .end = 0x2C000000 + 0x7F, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_PF10, + .end = IRQ_PF10, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct platform_device net2272_bfin_device = { + .name = "net2272", + .id = -1, + .num_resources = ARRAY_SIZE(net2272_bfin_resources), + .resource = net2272_bfin_resources, +}; +#endif + /* * USB-LAN EzExtender board * Driver needs to know address, irq and flag pin. @@ -204,6 +266,13 @@ static struct bfin5xx_spi_chip ad1836_spi_chip_info = { .bits_per_word = 16, }; #endif + +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif #endif /* SPI (0) */ @@ -248,6 +317,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &ad1836_spi_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; #if defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE) @@ -340,6 +418,10 @@ static struct platform_device *ezkit_devices[] __initdata = { &ax88180_device, #endif +#if defined(CONFIG_USB_NET2272) || defined(CONFIG_USB_NET2272_MODULE) + &net2272_bfin_device, +#endif + #if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE) &bfin_spi0_device, #endif @@ -359,6 +441,11 @@ static struct platform_device *ezkit_devices[] __initdata = { #if defined(CONFIG_I2C_GPIO) || defined(CONFIG_I2C_GPIO_MODULE) &i2c_gpio_device, #endif + +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) + &isp1362_hcd_device, +#endif + &ezkit_flash_device, }; diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile index 8636d4284bd..15e33ca1ce8 100644 --- a/arch/blackfin/mach-common/Makefile +++ b/arch/blackfin/mach-common/Makefile @@ -4,8 +4,6 @@ obj-y := \ cache.o cacheinit.o entry.o \ - interrupt.o lock.o irqpanic.o arch_checks.o + interrupt.o lock.o irqpanic.o arch_checks.o ints-priority.o -obj-$(CONFIG_BFIN_SINGLE_CORE) += ints-priority-sc.o -obj-$(CONFIG_BFIN_DUAL_CORE) += ints-priority-dc.o obj-$(CONFIG_PM) += pm.o dpmc.o diff --git a/arch/blackfin/mach-common/dpmc.S b/arch/blackfin/mach-common/dpmc.S index b82c096e198..b80ddd8b232 100644 --- a/arch/blackfin/mach-common/dpmc.S +++ b/arch/blackfin/mach-common/dpmc.S @@ -191,6 +191,9 @@ ENTRY(_sleep_mode) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; P0.H = hi(PLL_CTL); @@ -237,6 +240,10 @@ ENTRY(_deep_sleep) CLI R4; + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; call _set_dram_srfs; @@ -261,6 +268,9 @@ ENTRY(_deep_sleep) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; P0.H = hi(PLL_CTL); @@ -286,7 +296,13 @@ ENTRY(_sleep_deeper) CLI R4; P3 = R0; + P4 = R1; + P5 = R2; + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; call _set_dram_srfs; /* Set SDRAM Self Refresh */ @@ -327,6 +343,8 @@ ENTRY(_sleep_deeper) call _test_pll_locked; R0 = P3; + R1 = P4; + R3 = P5; call _set_sic_iwr; /* Set Awake from IDLE */ P0.H = hi(PLL_CTL); @@ -340,6 +358,9 @@ ENTRY(_sleep_deeper) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; /* Set Awake from IDLE PLL */ P0.H = hi(VR_CTL); @@ -417,14 +438,23 @@ ENTRY(_unset_dram_srfs) RTS; ENTRY(_set_sic_iwr) -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) P0.H = hi(SIC_IWR0); P0.L = lo(SIC_IWR0); + P1.H = hi(SIC_IWR1); + P1.L = lo(SIC_IWR1); + [P1] = R1; +#if defined(CONFIG_BF54x) + P1.H = hi(SIC_IWR2); + P1.L = lo(SIC_IWR2); + [P1] = R2; +#endif #else P0.H = hi(SIC_IWR); P0.L = lo(SIC_IWR); #endif [P0] = R0; + SSYNC; RTS; diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index fdd9bf43361..2cbb7a0bc38 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -121,6 +121,7 @@ ENTRY(_ex_icplb_miss) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SAVE_ALL_SYS + DEBUG_HWTRACE_SAVE(p5, r7) #ifdef CONFIG_MPU R0 = SEQSTAT; R1 = SP; @@ -132,14 +133,13 @@ ENTRY(_ex_icplb_miss) #else call __cplb_hdr; #endif - DEBUG_START_HWTRACE(p5, r7) + DEBUG_HWTRACE_RESTORE(p5, r7) RESTORE_ALL_SYS SP = EX_SCRATCH_REG; rtx; ENDPROC(_ex_icplb_miss) ENTRY(_ex_syscall) - DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; raise 15; /* invoked by TRAP #0, for sys call */ @@ -178,7 +178,6 @@ ENTRY(_ex_single_step) ENDPROC(_ex_single_step) ENTRY(_bfin_return_from_exception) - DEBUG_START_HWTRACE(p5, r7) #if ANOMALY_05000257 R7=LC0; LC0=R7; @@ -200,10 +199,9 @@ ENTRY(_handle_bad_cplb) * need to make a CPLB exception look like a normal exception */ - DEBUG_START_HWTRACE(p5, r7) RESTORE_ALL_SYS [--sp] = ASTAT; - [--sp] = (R7:6, P5:4); + [--sp] = (R7:6,P5:4); ENTRY(_ex_replaceable) nop; @@ -253,7 +251,6 @@ ENTRY(_ex_trap_c) R6 = SEQSTAT; [P5] = R6; - DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SP = EX_SCRATCH_REG; @@ -382,8 +379,7 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ sp.h = _exception_stack_top; /* Try to deal with syscalls quickly. */ [--sp] = ASTAT; - [--sp] = (R7:6, P5:4); - DEBUG_STOP_HWTRACE(p5, r7) + [--sp] = (R7:6,P5:4); r7 = SEQSTAT; /* reason code is in bit 5:0 */ r6.l = lo(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE); diff --git a/arch/blackfin/mach-common/ints-priority-dc.c b/arch/blackfin/mach-common/ints-priority-dc.c deleted file mode 100644 index 8d18d6b163b..00000000000 --- a/arch/blackfin/mach-common/ints-priority-dc.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * File: arch/blackfin/mach-common/ints-priority-dc.c - * Based on: - * Author: - * - * Created: ? - * Description: Set up the interrupt priorities - * - * Modified: - * 1996 Roman Zippel - * 1999 D. Jeff Dionne <jeff@uclinux.org> - * 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> - * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> - * 2003 Metrowerks/Motorola - * 2003 Bas Vermeulen <bas@buyways.nl> - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/kernel_stat.h> -#include <linux/seq_file.h> -#include <linux/irq.h> -#ifdef CONFIG_KGDB -#include <linux/kgdb.h> -#endif -#include <asm/traps.h> -#include <asm/blackfin.h> -#include <asm/gpio.h> -#include <asm/irq_handler.h> - -/* - * NOTES: - * - we have separated the physical Hardware interrupt from the - * levels that the LINUX kernel sees (see the description in irq.h) - * - - */ - -/* Initialize this to an actual value to force it into the .data - * section so that we know it is properly initialized at entry into - * the kernel but before bss is initialized to zero (which is where - * it would live otherwise). The 0x1f magic represents the IRQs we - * cannot actually mask out in hardware. - */ -unsigned long irq_flags = 0x1f; - -/* The number of spurious interrupts */ -atomic_t num_spurious; - -struct ivgx { - /* irq number for request_irq, available in mach-bf561/irq.h */ - int irqno; - /* corresponding bit in the SICA_ISR0 register */ - int isrflag0; - /* corresponding bit in the SICA_ISR1 register */ - int isrflag1; -} ivg_table[NR_PERI_INTS]; - -struct ivg_slice { - /* position of first irq in ivg_table for given ivg */ - struct ivgx *ifirst; - struct ivgx *istop; -} ivg7_13[IVG13 - IVG7 + 1]; - -static void search_IAR(void); - -/* - * Search SIC_IAR and fill tables with the irqvalues - * and their positions in the SIC_ISR register. - */ -static void __init search_IAR(void) -{ - unsigned ivg, irq_pos = 0; - for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { - int irqn; - - ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos]; - - for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { - int iar_shift = (irqn & 7) * 4; - if (ivg == - (0xf & - bfin_read32((unsigned long *)SICA_IAR0 + - (irqn >> 3)) >> iar_shift)) { - ivg_table[irq_pos].irqno = IVG7 + irqn; - ivg_table[irq_pos].isrflag0 = - (irqn < 32 ? (1 << irqn) : 0); - ivg_table[irq_pos].isrflag1 = - (irqn < 32 ? 0 : (1 << (irqn - 32))); - ivg7_13[ivg].istop++; - irq_pos++; - } - } - } -} - -/* - * This is for BF561 internal IRQs - */ - -static void ack_noop(unsigned int irq) -{ - /* Dummy function. */ -} - -static void bf561_core_mask_irq(unsigned int irq) -{ - irq_flags &= ~(1 << irq); - if (!irqs_disabled()) - local_irq_enable(); -} - -static void bf561_core_unmask_irq(unsigned int irq) -{ - irq_flags |= 1 << irq; - /* - * If interrupts are enabled, IMASK must contain the same value - * as irq_flags. Make sure that invariant holds. If interrupts - * are currently disabled we need not do anything; one of the - * callers will take care of setting IMASK to the proper value - * when reenabling interrupts. - * local_irq_enable just does "STI irq_flags", so it's exactly - * what we need. - */ - if (!irqs_disabled()) - local_irq_enable(); - return; -} - -static void bf561_internal_mask_irq(unsigned int irq) -{ - unsigned long irq_mask; - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() & ~irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() & ~irq_mask); - } -} - -static void bf561_internal_unmask_irq(unsigned int irq) -{ - unsigned long irq_mask; - - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() | irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() | irq_mask); - } - SSYNC(); -} - -static struct irq_chip bf561_core_irqchip = { - .ack = ack_noop, - .mask = bf561_core_mask_irq, - .unmask = bf561_core_unmask_irq, -}; - -static struct irq_chip bf561_internal_irqchip = { - .ack = ack_noop, - .mask = bf561_internal_mask_irq, - .unmask = bf561_internal_unmask_irq, -}; - -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; - -static void bf561_gpio_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } -} - -static void bf561_gpio_mask_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } - - set_gpio_maska(gpionr, 0); - SSYNC(); -} - -static void bf561_gpio_mask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 0); - SSYNC(); -} - -static void bf561_gpio_unmask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 1); - SSYNC(); -} - -static unsigned int bf561_gpio_irq_startup(unsigned int irq) -{ - unsigned int ret; - char buf[8]; - u16 gpionr = irq - IRQ_PF0; - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - snprintf(buf, sizeof buf, "IRQ %d", irq); - ret = gpio_request(gpionr, buf); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bf561_gpio_unmask_irq(irq); - - return ret; - -} - -static void bf561_gpio_irq_shutdown(unsigned int irq) -{ - bf561_gpio_mask_irq(irq); - gpio_free(irq - IRQ_PF0); - gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); -} - -static int bf561_gpio_irq_type(unsigned int irq, unsigned int type) -{ - - unsigned int ret; - char buf[8]; - u16 gpionr = irq - IRQ_PF0; - - - if (type == IRQ_TYPE_PROBE) { - /* only probe unenabled GPIO interrupt lines */ - if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr)) - return 0; - type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; - - } - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | - IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - snprintf(buf, sizeof buf, "IRQ %d", irq); - ret = gpio_request(gpionr, buf); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; - } - - - set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } - - if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_gpio_both(gpionr, 1); - else - set_gpio_both(gpionr, 0); - - if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) - set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */ - else - set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ - - SSYNC(); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_irq_handler(irq, handle_edge_irq); - else - set_irq_handler(irq, handle_level_irq); - - return 0; -} - -static struct irq_chip bf561_gpio_irqchip = { - .ack = bf561_gpio_ack_irq, - .mask = bf561_gpio_mask_irq, - .mask_ack = bf561_gpio_mask_ack_irq, - .unmask = bf561_gpio_unmask_irq, - .set_type = bf561_gpio_irq_type, - .startup = bf561_gpio_irq_startup, - .shutdown = bf561_gpio_irq_shutdown -}; - -static void bf561_demux_gpio_irq(unsigned int inta_irq, - struct irq_desc *intb_desc) -{ - int irq, flag_d, mask; - u16 gpio; - - switch (inta_irq) { - case IRQ_PROG0_INTA: - irq = IRQ_PF0; - break; - case IRQ_PROG1_INTA: - irq = IRQ_PF16; - break; - case IRQ_PROG2_INTA: - irq = IRQ_PF32; - break; - default: - dump_stack(); - return; - } - - gpio = irq - IRQ_PF0; - - flag_d = get_gpiop_data(gpio); - mask = flag_d & (gpio_enabled[gpio_bank(gpio)] & - get_gpiop_maska(gpio)); - - do { - if (mask & 1) { - struct irq_desc *desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - irq++; - mask >>= 1; - } while (mask); - - -} - -void __init init_exception_vectors(void) -{ - SSYNC(); - - /* cannot program in software: - * evt0 - emulation (jtag) - * evt1 - reset - */ - bfin_write_EVT2(evt_nmi); - bfin_write_EVT3(trap); - bfin_write_EVT5(evt_ivhw); - bfin_write_EVT6(evt_timer); - bfin_write_EVT7(evt_evt7); - bfin_write_EVT8(evt_evt8); - bfin_write_EVT9(evt_evt9); - bfin_write_EVT10(evt_evt10); - bfin_write_EVT11(evt_evt11); - bfin_write_EVT12(evt_evt12); - bfin_write_EVT13(evt_evt13); - bfin_write_EVT14(evt14_softirq); - bfin_write_EVT15(evt_system_call); - CSYNC(); -} - -/* - * This function should be called during kernel startup to initialize - * the BFin IRQ handling routines. - */ -int __init init_arch_irq(void) -{ - int irq; - unsigned long ilat = 0; - /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ - bfin_write_SICA_IMASK0(SIC_UNMASK_ALL); - bfin_write_SICA_IMASK1(SIC_UNMASK_ALL); - SSYNC(); - - bfin_write_SICA_IWR0(IWR_ENABLE_ALL); - bfin_write_SICA_IWR1(IWR_ENABLE_ALL); - - local_irq_disable(); - - init_exception_buff(); - - for (irq = 0; irq <= SYS_IRQS; irq++) { - if (irq <= IRQ_CORETMR) - set_irq_chip(irq, &bf561_core_irqchip); - else - set_irq_chip(irq, &bf561_internal_irqchip); - - if ((irq != IRQ_PROG0_INTA) && - (irq != IRQ_PROG1_INTA) && - (irq != IRQ_PROG2_INTA)) - set_irq_handler(irq, handle_simple_irq); - else - set_irq_chained_handler(irq, bf561_demux_gpio_irq); - } - - for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) { - set_irq_chip(irq, &bf561_gpio_irqchip); - /* if configured as edge, then will be changed to do_edge_IRQ */ - set_irq_handler(irq, handle_level_irq); - } - - bfin_write_IMASK(0); - CSYNC(); - ilat = bfin_read_ILAT(); - CSYNC(); - bfin_write_ILAT(ilat); - CSYNC(); - - printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); - /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, - * local_irq_enable() - */ - program_IAR(); - /* Therefore it's better to setup IARs before interrupts enabled */ - search_IAR(); - - /* Enable interrupts IVG7-15 */ - irq_flags = irq_flags | IMASK_IVG15 | - IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | - IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; - - return 0; -} - -#ifdef CONFIG_DO_IRQ_L1 -__attribute__((l1_text)) -#endif -void do_irq(int vec, struct pt_regs *fp) -{ - if (vec == EVT_IVTMR_P) { - vec = IRQ_CORETMR; - } else { - struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; - struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; - unsigned long sic_status0, sic_status1; - - SSYNC(); - sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0(); - sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1(); - - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } else if ((sic_status0 & ivg->isrflag0) || - (sic_status1 & ivg->isrflag1)) - break; - } - vec = ivg->irqno; - } - asm_do_IRQ(vec, fp); - -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); -#endif -} diff --git a/arch/blackfin/mach-common/ints-priority-sc.c b/arch/blackfin/mach-common/ints-priority.c index dec42acb5de..880595afe98 100644 --- a/arch/blackfin/mach-common/ints-priority-sc.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -1,5 +1,5 @@ /* - * File: arch/blackfin/mach-common/ints-priority-sc.c + * File: arch/blackfin/mach-common/ints-priority.c * Based on: * Author: * @@ -13,7 +13,7 @@ * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> * 2003 Metrowerks/Motorola * 2003 Bas Vermeulen <bas@buyways.nl> - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -69,6 +69,10 @@ unsigned long irq_flags = 0x1f; /* The number of spurious interrupts */ atomic_t num_spurious; +#ifdef CONFIG_PM +unsigned long bfin_sic_iwr[3]; /* Up to 3 SIC_IWRx registers */ +#endif + struct ivgx { /* irq number for request_irq, available in mach-bf533/irq.h */ unsigned int irqno; @@ -98,8 +102,7 @@ static void __init search_IAR(void) for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { int iar_shift = (irqn & 7) * 4; - if (ivg == - (0xf & + if (ivg == (0xf & #ifndef CONFIG_BF52x bfin_read32((unsigned long *)SIC_IAR0 + (irqn >> 3)) >> iar_shift)) { @@ -179,6 +182,27 @@ static void bfin_internal_unmask_irq(unsigned int irq) SSYNC(); } +#ifdef CONFIG_PM +int bfin_internal_set_wake(unsigned int irq, unsigned int state) +{ + unsigned bank, bit; + unsigned long flags; + bank = (irq - (IRQ_CORETMR + 1)) / 32; + bit = (irq - (IRQ_CORETMR + 1)) % 32; + + local_irq_save(flags); + + if (state) + bfin_sic_iwr[bank] |= (1 << bit); + else + bfin_sic_iwr[bank] &= ~(1 << bit); + + local_irq_restore(flags); + + return 0; +} +#endif + static struct irq_chip bfin_core_irqchip = { .ack = ack_noop, .mask = bfin_core_mask_irq, @@ -189,6 +213,9 @@ static struct irq_chip bfin_internal_irqchip = { .ack = ack_noop, .mask = bfin_internal_mask_irq, .unmask = bfin_internal_unmask_irq, +#ifdef CONFIG_PM + .set_wake = bfin_internal_set_wake, +#endif }; #ifdef BF537_GENERIC_ERROR_INT_DEMUX @@ -206,8 +233,7 @@ static void bfin_generic_error_mask_irq(unsigned int irq) if (!error_int_mask) { local_irq_disable(); bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & - ~(1 << - (IRQ_GENERIC_ERROR - + ~(1 << (IRQ_GENERIC_ERROR - (IRQ_CORETMR + 1)))); SSYNC(); local_irq_enable(); @@ -232,7 +258,7 @@ static struct irq_chip bfin_generic_error_irqchip = { }; static void bfin_demux_error_irq(unsigned int int_err_irq, - struct irq_desc *intb_desc) + struct irq_desc *inta_desc) { int irq = 0; @@ -404,16 +430,8 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } + set_gpio_inen(gpionr, 0); set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) @@ -426,6 +444,18 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) else set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + set_gpio_edge(gpionr, 1); + set_gpio_inen(gpionr, 1); + gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); + set_gpio_data(gpionr, 0); + + } else { + set_gpio_edge(gpionr, 0); + gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + set_gpio_inen(gpionr, 1); + } + SSYNC(); if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) @@ -436,6 +466,20 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } +#ifdef CONFIG_PM +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + unsigned gpio = irq_to_gpio(irq); + + if (state) + gpio_pm_wakeup_request(gpio, PM_WAKE_IGNORE); + else + gpio_pm_wakeup_free(gpio); + + return 0; +} +#endif + static struct irq_chip bfin_gpio_irqchip = { .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, @@ -443,30 +487,87 @@ static struct irq_chip bfin_gpio_irqchip = { .unmask = bfin_gpio_unmask_irq, .set_type = bfin_gpio_irq_type, .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif }; -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) { - u16 i; - struct irq_desc *desc; + unsigned int i, gpio, mask, irq, search = 0; - for (i = 0; i < MAX_BLACKFIN_GPIOS; i += 16) { - int irq = IRQ_PF0 + i; - int flag_d = get_gpiop_data(i); - int mask = - flag_d & (gpio_enabled[gpio_bank(i)] & get_gpiop_maska(i)); + switch (inta_irq) { +#if defined(CONFIG_BF53x) + case IRQ_PROG_INTA: + irq = IRQ_PF0; + search = 1; + break; +# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) + case IRQ_MAC_RX: + irq = IRQ_PH0; + break; +# endif +#elif defined(CONFIG_BF52x) + case IRQ_PORTF_INTA: + irq = IRQ_PF0; + break; + case IRQ_PORTG_INTA: + irq = IRQ_PG0; + break; + case IRQ_PORTH_INTA: + irq = IRQ_PH0; + break; +#elif defined(CONFIG_BF561) + case IRQ_PROG0_INTA: + irq = IRQ_PF0; + break; + case IRQ_PROG1_INTA: + irq = IRQ_PF16; + break; + case IRQ_PROG2_INTA: + irq = IRQ_PF32; + break; +#endif + default: + BUG(); + return; + } - while (mask) { - if (mask & 1) { - desc = irq_desc + irq; - desc->handle_irq(irq, desc); + if (search) { + for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { + irq += i; + + mask = get_gpiop_data(i) & + (gpio_enabled[gpio_bank(i)] & + get_gpiop_maska(i)); + + while (mask) { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; } - irq++; - mask >>= 1; } + } else { + gpio = irq_to_gpio(irq); + mask = get_gpiop_data(gpio) & + (gpio_enabled[gpio_bank(gpio)] & + get_gpiop_maska(gpio)); + + do { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; + } while (mask); } + } #else /* CONFIG_BF54x */ @@ -711,6 +812,74 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } +#ifdef CONFIG_PM +u32 pint_saved_masks[NR_PINT_SYS_IRQS]; +u32 pint_wakeup_masks[NR_PINT_SYS_IRQS]; + +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + u32 pint_irq; + u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; + u32 bank = PINT_2_BANK(pint_val); + u32 pintbit = PINT_BIT(pint_val); + + switch (bank) { + case 0: + pint_irq = IRQ_PINT0; + break; + case 2: + pint_irq = IRQ_PINT2; + break; + case 3: + pint_irq = IRQ_PINT3; + break; + case 1: + pint_irq = IRQ_PINT1; + break; + default: + return -EINVAL; + } + + bfin_internal_set_wake(pint_irq, state); + + if (state) + pint_wakeup_masks[bank] |= pintbit; + else + pint_wakeup_masks[bank] &= ~pintbit; + + return 0; +} + +u32 bfin_pm_setup(void) +{ + u32 val, i; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint[i]->mask_clear; + pint_saved_masks[i] = val; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = val; + pint[i]->mask_set = pint_wakeup_masks[i]; + } + } + + return 0; +} + +void bfin_pm_restore(void) +{ + u32 i, val; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint_saved_masks[i]; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = pint[i]->mask_clear; + pint[i]->mask_set = val; + } + } +} +#endif + static struct irq_chip bfin_gpio_irqchip = { .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, @@ -718,17 +887,19 @@ static struct irq_chip bfin_gpio_irqchip = { .unmask = bfin_gpio_unmask_irq, .set_type = bfin_gpio_irq_type, .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif }; -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) { u8 bank, pint_val; u32 request, irq; - struct irq_desc *desc; - switch (intb_irq) { + switch (inta_irq) { case IRQ_PINT0: bank = 0; break; @@ -795,7 +966,7 @@ int __init init_arch_irq(void) int irq; unsigned long ilat = 0; /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); bfin_write_SIC_IWR0(IWR_ENABLE_ALL); @@ -812,6 +983,8 @@ int __init init_arch_irq(void) local_irq_disable(); + init_exception_buff(); + #ifdef CONFIG_BF54x # ifdef CONFIG_PINTx_REASSIGN pint[0]->assign = CONFIG_PINT0_ASSIGN; @@ -874,6 +1047,19 @@ int __init init_arch_irq(void) set_irq_chained_handler(irq, bfin_demux_gpio_irq); break; +#elif defined(CONFIG_BF561) + case IRQ_PROG0_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; + case IRQ_PROG1_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; + case IRQ_PROG2_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; #endif default: set_irq_handler(irq, handle_simple_irq); @@ -893,11 +1079,8 @@ int __init init_arch_irq(void) } #endif -#ifndef CONFIG_BF54x - for (irq = IRQ_PF0; irq < NR_IRQS; irq++) { -#else - for (irq = IRQ_PA0; irq < NR_IRQS; irq++) { -#endif + for (irq = GPIO_IRQ_BASE; irq < NR_IRQS; irq++) { + set_irq_chip(irq, &bfin_gpio_irqchip); /* if configured as edge, then will be changed to do_edge_IRQ */ set_irq_handler(irq, handle_level_irq); @@ -936,7 +1119,7 @@ void do_irq(int vec, struct pt_regs *fp) } else { struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) unsigned long sic_status[3]; SSYNC(); diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c index 81930f7d06f..0be805ca423 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c @@ -4,7 +4,7 @@ * Author: Cliff Brake <cbrake@accelent.com> Copyright (c) 2001 * * Created: 2001 - * Description: Power management for the bfin + * Description: Blackfin power management * * Modified: Nicolas Pitre - PXA250 support * Copyright (c) 2002 Monta Vista Software, Inc. @@ -12,7 +12,7 @@ * Copyright (c) 2002 Monta Vista Software, Inc. * Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610 * Copyright 2004 - * Copyright 2004-2006 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -67,42 +67,30 @@ void bfin_pm_suspend_standby_enter(void) gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); #endif -#if defined(CONFIG_PM_WAKEUP_BY_GPIO) || defined(CONFIG_PM_WAKEUP_GPIO_API) - { - u32 flags; + u32 flags; - local_irq_save(flags); + local_irq_save(flags); + bfin_pm_setup(); - sleep_deeper(gpio_pm_setup()); /*Goto Sleep*/ - - gpio_pm_restore(); - -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) - bfin_write_SIC_IWR0(IWR_ENABLE_ALL); - bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x - bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -# endif +#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER + sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); #else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); + sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); #endif - local_irq_restore(flags); - } -#endif + bfin_pm_restore(); -#if defined(CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR) - sleep_deeper(CONFIG_PM_WAKEUP_SIC_IWR); -# if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) bfin_write_SIC_IWR0(IWR_ENABLE_ALL); bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x +# ifdef CONFIG_BF54x bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -# endif -# else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); # endif -#endif /* CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR */ +#else + bfin_write_SIC_IWR(IWR_ENABLE_ALL); +#endif + + local_irq_restore(flags); } /* diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index eb1a12ac9e3..1f516c55bde 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -138,8 +138,7 @@ void __init mem_init(void) start_mem = PAGE_ALIGN(start_mem); max_mapnr = num_physpages = MAP_NR(high_memory); - printk(KERN_INFO "Kernel managed physical pages: %lu\n", - num_physpages); + printk(KERN_DEBUG "Kernel managed physical pages: %lu\n", num_physpages); /* This will put all memory onto the freelists. */ totalram_pages = free_all_bootmem(); @@ -153,8 +152,7 @@ void __init mem_init(void) /* do not count in kernel image between _rambase and _ramstart */ reservedpages -= (_ramstart - _rambase) >> PAGE_SHIFT; #if (defined(CONFIG_BFIN_ICACHE) && ANOMALY_05000263) - reservedpages += (_ramend - memory_end - DMA_UNCACHED_REGION) >> - PAGE_SHIFT; + reservedpages += (_ramend - memory_end - DMA_UNCACHED_REGION) >> PAGE_SHIFT; #endif codek = (_etext - _stext) >> 10; @@ -163,11 +161,9 @@ void __init mem_init(void) printk(KERN_INFO "Memory available: %luk/%luk RAM, " - "(%uk init code, %uk kernel code, " - "%uk data, %uk dma, %uk reserved)\n", + "(%uk init code, %uk kernel code, %uk data, %uk dma, %uk reserved)\n", (unsigned long) freepages << (PAGE_SHIFT-10), _ramend >> 10, - initk, codek, datak, DMA_UNCACHED_REGION >> 10, - (reservedpages << (PAGE_SHIFT-10))); + initk, codek, datak, DMA_UNCACHED_REGION >> 10, (reservedpages << (PAGE_SHIFT-10))); /* Initialize the blackfin L1 Memory. */ l1sram_init(); diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index 27b082ac7f1..8456bc8efb7 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -13,10 +13,6 @@ config ZONE_DMA bool default y -config NO_DMA - bool - default y - config RWSEM_GENERIC_SPINLOCK bool default y @@ -24,6 +20,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_IOMAP + bool + default y + config ARCH_HAS_ILOG2_U32 bool default n @@ -44,17 +44,21 @@ config GENERIC_CALIBRATE_DELAY bool default y -config IRQ_PER_CPU - bool - default y - config NO_IOPORT def_bool y +config FORCE_MAX_ZONEORDER + int + default 6 + config CRIS bool default y +config HZ + int + default 100 + source "init/Kconfig" menu "General setup" @@ -93,17 +97,15 @@ config ETRAX_FAST_TIMER timers). This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled. -config PREEMPT - bool "Preemptible Kernel" +config ETRAX_KMALLOCED_MODULES + bool "Enable module allocation with kmalloc" help - This option reduces the latency of the kernel when reacting to - real-time or interactive events by allowing a low priority process to - be preempted even if it is in kernel mode executing a system call. - This allows applications to run more reliably even when the system is - under load. + Enable module allocation with kmalloc instead of vmalloc. + +config OOM_REBOOT + bool "Enable reboot at out of memory" - Say Y here if you are building a kernel for a desktop, embedded - or real-time system. Say N if you are unsure. +source "kernel/Kconfig.preempt" source mm/Kconfig @@ -130,24 +132,124 @@ config SVINTO_SIM help Support the xsim ETRAX Simulator. +config ETRAXFS + bool "ETRAX-FS-V32" + help + Support CRIS V32. + +config CRIS_MACH_ARTPEC3 + bool "ARTPEC-3" + help + Support Axis ARTPEC-3. + endchoice +config ETRAX_VCS_SIM + bool "VCS Simulator" + help + Setup hardware to be run in the VCS simulator. + config ETRAX_ARCH_V10 bool default y if ETRAX100LX || ETRAX100LX_V2 default n if !(ETRAX100LX || ETRAX100LX_V2) +config ETRAX_ARCH_V32 + bool + default y if (ETRAXFS || CRIS_MACH_ARTPEC3) + default n if !(ETRAXFS || CRIS_MACH_ARTPEC3) + config ETRAX_DRAM_SIZE int "DRAM size (dec, in MB)" default "8" help Size of DRAM (decimal in MB) typically 2, 8 or 16. +config ETRAX_VMEM_SIZE + int "Video memory size (dec, in MB)" + depends on ETRAX_ARCH_V32 && !ETRAXFS + default 8 if !ETRAXFS + help + Size of Video accessible memory (decimal, in MB). + config ETRAX_FLASH_BUSWIDTH - int "Buswidth of flash in bytes" + int "Buswidth of NOR flash in bytes" default "2" help - Width in bytes of the Flash bus (1, 2 or 4). Is usually 2. + Width in bytes of the NOR Flash bus (1, 2 or 4). Is usually 2. + +config ETRAX_NANDFLASH_BUSWIDTH + int "Buswidth of NAND flash in bytes" + default "1" + help + Width in bytes of the NAND flash (1 or 2). + +config ETRAX_FLASH1_SIZE + int "FLASH1 size (dec, in MB. 0 = Unknown)" + default "0" + +choice + prompt "Product debug-port" + default ETRAX_DEBUG_PORT0 + +config ETRAX_DEBUG_PORT0 + bool "Serial-0" + help + Choose a serial port for the ETRAX debug console. Default to + port 0. + +config ETRAX_DEBUG_PORT1 + bool "Serial-1" + help + Use serial port 1 for the console. + +config ETRAX_DEBUG_PORT2 + bool "Serial-2" + help + Use serial port 2 for the console. + +config ETRAX_DEBUG_PORT3 + bool "Serial-3" + help + Use serial port 3 for the console. + +config ETRAX_DEBUG_PORT_NULL + bool "disabled" + help + Disable serial-port debugging. + +endchoice + +choice + prompt "Kernel GDB port" + depends on ETRAX_KGDB + default ETRAX_KGDB_PORT0 + help + Choose a serial port for kernel debugging. NOTE: This port should + not be enabled under Drivers for built-in interfaces (as it has its + own initialization code) and should not be the same as the debug port. + +config ETRAX_KGDB_PORT0 + bool "Serial-0" + help + Use serial port 0 for kernel debugging. + +config ETRAX_KGDB_PORT1 + bool "Serial-1" + help + Use serial port 1 for kernel debugging. + +config ETRAX_KGDB_PORT2 + bool "Serial-2" + help + Use serial port 2 for kernel debugging. + +config ETRAX_KGDB_PORT3 + bool "Serial-3" + help + Use serial port 3 for kernel debugging. + +endchoice source arch/cris/arch-v10/Kconfig source arch/cris/arch-v32/Kconfig @@ -161,6 +263,387 @@ menu "Drivers for built-in interfaces" source arch/cris/arch-v10/drivers/Kconfig source arch/cris/arch-v32/drivers/Kconfig +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE if ETRAX_ARCH_V32 + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_RTC + bool "Real Time Clock support" + depends on ETRAX_I2C + help + Enables drivers for the Real-Time Clock battery-backed chips on + some products. The kernel reads the time when booting, and + the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a + rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc + device. You can check the time with cat /proc/rtc, but + normal time reading should be done using libc function time and + friends. + +choice + prompt "RTC chip" + depends on ETRAX_RTC + default ETRAX_PCF8563 if ETRAX_ARCH_V32 + default ETRAX_DS1302 if ETRAX_ARCH_V10 + +config ETRAX_DS1302 + depends on ETRAX_ARCH_V10 + bool "DS1302" + help + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. + +config ETRAX_PCF8563 + bool "PCF8563" + help + Enables the driver for the PCF8563 Real-Time Clock battery-backed + chip on some products. + +endchoice + +config ETRAX_SYNCHRONOUS_SERIAL + bool "Synchronous serial-port support" + help + Select this to enable the synchronous serial port driver. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT0 + bool "Synchronous serial port 0 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL + help + Enabled synchronous serial port 0. + +config ETRAX_SYNCHRONOUS_SERIAL0_DMA + bool "Enable DMA on synchronous serial port 0." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT1 + bool "Synchronous serial port 1 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL && (ETRAXFS || ETRAX_ARCH_V10) + help + Enabled synchronous serial port 1. + +config ETRAX_SYNCHRONOUS_SERIAL1_DMA + bool "Enable DMA on synchronous serial port 1." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +choice + prompt "Network LED behavior" + depends on ETRAX_ETHERNET + default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + +config ETRAX_NETWORK_LED_ON_WHEN_LINK + bool "LED_on_when_link" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + bool "LED_on_when_activity" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +endchoice + +choice + prompt "Ser0 DMA out channel" + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_DMA6_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT0_NO_DMA_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT0_NO_DMA_OUT + bool "Ser0 uses no DMA for output" + help + Do not use DMA for ser0 output. + +config ETRAX_SERIAL_PORT0_DMA6_OUT + bool "Ser0 uses DMA6 for output" + depends on ETRAXFS + help + Enables the DMA6 output channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT0_DMA0_OUT + bool "Ser0 uses DMA0 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA0 output channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser0 DMA in channel " + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT0_DMA7_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser0. + +config ETRAX_SERIAL_PORT0_NO_DMA_IN + bool "Ser0 uses no DMA for input" + help + Do not use DMA for ser0 input. + +config ETRAX_SERIAL_PORT0_DMA7_IN + bool "Ser0 uses DMA7 for input" + depends on ETRAXFS + help + Enables the DMA7 input channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT0_DMA1_IN + bool "Ser0 uses DMA1 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA1 input channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser1 DMA in channel " + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT1_DMA9_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser1. + +config ETRAX_SERIAL_PORT1_NO_DMA_IN + bool "Ser1 uses no DMA for input" + help + Do not use DMA for ser1 input. + +config ETRAX_SERIAL_PORT1_DMA5_IN + bool "Ser1 uses DMA5 for input" + depends on ETRAX_ARCH_V32 + help + Enables the DMA5 input channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want this on, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT1_DMA9_IN + depends on ETRAX_ARCH_V10 + bool "Ser1 uses DMA9 for input" + +endchoice + + +choice + prompt "Ser1 DMA out channel" + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT1_DMA8_OUT if ETRAX_ARCH_V10 + help + What DMA channel to use for ser1. + +config ETRAX_SERIAL_PORT1_NO_DMA_OUT + bool "Ser1 uses no DMA for output" + help + Do not use DMA for ser1 output. + +config ETRAX_SERIAL_PORT1_DMA8_OUT + depends on ETRAX_ARCH_V10 + bool "Ser1 uses DMA8 for output" + +config ETRAX_SERIAL_PORT1_DMA4_OUT + depends on ETRAX_ARCH_V32 + bool "Ser1 uses DMA4 for output" + help + Enables the DMA4 output channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want this on, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser2 DMA out channel" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT2_DMA2_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT2_NO_DMA_OUT + bool "Ser2 uses no DMA for output" + help + Do not use DMA for ser2 output. + +config ETRAX_SERIAL_PORT2_DMA2_OUT + bool "Ser2 uses DMA2 for output" + depends on ETRAXFS || ETRAX_ARCH_V10 + help + Enables the DMA2 output channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT2_DMA6_OUT + bool "Ser2 uses DMA6 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA6 output channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser2 DMA in channel" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT2_DMA3_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser2. + +config ETRAX_SERIAL_PORT2_NO_DMA_IN + bool "Ser2 uses no DMA for input" + help + Do not use DMA for ser2 input. + +config ETRAX_SERIAL_PORT2_DMA3_IN + bool "Ser2 uses DMA3 for input" + depends on ETRAXFS || ETRAX_ARCH_V10 + help + Enables the DMA3 input channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT2_DMA7_IN + bool "Ser2 uses DMA7 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA7 input channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser3 DMA in channel" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT3_DMA5_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser3. + +config ETRAX_SERIAL_PORT3_NO_DMA_IN + bool "Ser3 uses no DMA for input" + help + Do not use DMA for ser3 input. + +config ETRAX_SERIAL_PORT3_DMA5_IN + depends on ETRAX_ARCH_V10 + bool "DMA 5" + +config ETRAX_SERIAL_PORT3_DMA9_IN + bool "Ser3 uses DMA9 for input" + depends on ETRAXFS + help + Enables the DMA9 input channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT3_DMA3_IN + bool "Ser3 uses DMA3 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA3 input channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser3 DMA out channel" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT3_DMA4_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT3_NO_DMA_OUT + bool "Ser3 uses no DMA for output" + help + Do not use DMA for ser3 output. + +config ETRAX_SERIAL_PORT3_DMA4_OUT + depends on ETRAX_ARCH_V10 + bool "DMA 4" + +config ETRAX_SERIAL_PORT3_DMA8_OUT + bool "Ser3 uses DMA8 for output" + depends on ETRAXFS + help + Enables the DMA8 output channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT3_DMA2_OUT + bool "Ser3 uses DMA2 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA2 output channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + endmenu source "drivers/base/Kconfig" @@ -174,22 +657,10 @@ source "drivers/pnp/Kconfig" source "drivers/block/Kconfig" -source "drivers/md/Kconfig" - source "drivers/ide/Kconfig" -source "drivers/scsi/Kconfig" - -source "drivers/ieee1394/Kconfig" - -source "drivers/message/i2o/Kconfig" - source "drivers/net/Kconfig" -source "drivers/isdn/Kconfig" - -source "drivers/telephony/Kconfig" - source "drivers/i2c/Kconfig" source "drivers/rtc/Kconfig" @@ -201,17 +672,8 @@ source "drivers/input/Kconfig" source "drivers/char/Kconfig" -#source drivers/misc/Config.in -source "drivers/media/Kconfig" - source "fs/Kconfig" -source "sound/Kconfig" - -source "drivers/pcmcia/Kconfig" - -source "drivers/pci/Kconfig" - source "drivers/usb/Kconfig" source "arch/cris/Kconfig.debug" diff --git a/arch/cris/Makefile b/arch/cris/Makefile index e6bf00c262e..838cd2ae03a 100644 --- a/arch/cris/Makefile +++ b/arch/cris/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $ +# # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -10,28 +10,36 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. -# A bug in ld prevents us from having a (constant-value) symbol in a -# "ORIGIN =" or "LENGTH =" expression. - arch-y := v10 arch-$(CONFIG_ETRAX_ARCH_V10) := v10 arch-$(CONFIG_ETRAX_ARCH_V32) := v32 -# No config avaiable for make clean etc +# No config available for make clean etc +mach-y := fs +mach-$(CONFIG_CRIS_MACH_ARTPEC3) := a3 +mach-$(CONFIG_ETRAXFS) := fs + ifneq ($(arch-y),) SARCH := arch-$(arch-y) else SARCH := endif +ifneq ($(mach-y),) +MACH := mach-$(mach-y) +else +MACH := +endif + LD = $(CROSS_COMPILE)ld -mcrislinux OBJCOPYFLAGS := -O binary -R .note -R .comment -S CPPFLAGS_vmlinux.lds = -DDRAM_VIRTUAL_BASE=0x$(CONFIG_ETRAX_DRAM_VIRTUAL_BASE) -KBUILD_AFLAGS += -mlinux -KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe +KBUILD_AFLAGS += -mlinux -march=$(arch-y) -Iinclude/asm/arch/mach -Iinclude/asm/arch + +KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe -Iinclude/asm/arch/mach -Iinclude/asm/arch ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS := $(subst -fomit-frame-pointer,,$(KBUILD_CFLAGS)) -g @@ -44,6 +52,9 @@ LIBGCC = $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libgcc.a) core-y += arch/$(ARCH)/kernel/ arch/$(ARCH)/mm/ core-y += arch/$(ARCH)/$(SARCH)/kernel/ arch/$(ARCH)/$(SARCH)/mm/ +ifdef CONFIG_ETRAX_ARCH_V32 +core-y += arch/$(ARCH)/$(SARCH)/$(MACH)/ +endif drivers-y += arch/$(ARCH)/$(SARCH)/drivers/ libs-y += arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC) @@ -52,79 +63,69 @@ SRC_ARCH = $(srctree)/arch/$(ARCH) # cris object files path OBJ_ARCH = $(objtree)/arch/$(ARCH) -target_boot_arch_dir = $(OBJ_ARCH)/$(SARCH)/boot -target_boot_dir = $(OBJ_ARCH)/boot -src_boot_dir = $(SRC_ARCH)/boot -target_compressed_dir = $(OBJ_ARCH)/boot/compressed -src_compressed_dir = $(SRC_ARCH)/boot/compressed -target_rescue_dir = $(OBJ_ARCH)/boot/rescue -src_rescue_dir = $(SRC_ARCH)/boot/rescue - -export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir - -vmlinux.bin: vmlinux - $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin - -timage: vmlinux.bin - cat vmlinux.bin cramfs.img >timage - -simimage: timage - cp vmlinux.bin simvmlinux.bin - -# the following will remake timage without compiling the kernel -# it does of course require that all object files exist... - -cramfs: -## cramfs - Creates a cramfs image - mkcramfs -b 8192 -m romfs_meta.txt root cramfs.img - cat vmlinux.bin cramfs.img >timage +boot := arch/$(ARCH)/boot +MACHINE := arch/$(ARCH)/$(SARCH) -clinux: vmlinux.bin decompress.bin rescue.bin +all: zImage -decompress.bin: $(target_boot_dir) - @$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin +zImage Image: vmlinux + $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ -$(target_rescue_dir)/rescue.bin: $(target_boot_dir) - @$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin - -zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin -## zImage - Compressed kernel (gzip) - @$(MAKE) -f $(src_boot_dir)/Makefile zImage - -$(target_boot_dir): $(target_boot_arch_dir) - ln -sfn $< $@ - -$(target_boot_arch_dir): - mkdir -p $@ - -compressed: zImage - -archmrproper: -archclean: - @if [ -d arch/$(ARCH)/boot ]; then \ - $(MAKE) $(clean)=arch/$(ARCH)/boot ; \ - fi - rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img - rm -rf $(LD_SCRIPT).tmp - -archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch +archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch FORCE # Create some links to make all tools happy $(SRC_ARCH)/.links: @rm -rf $(SRC_ARCH)/drivers - @ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers + @ln -sfn $(SARCH)/drivers $(SRC_ARCH)/drivers @rm -rf $(SRC_ARCH)/boot - @ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot + @ln -sfn $(SARCH)/boot $(SRC_ARCH)/boot @rm -rf $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch - @ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S - @ln -sfn $(SRC_ARCH)/$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn $(SARCH)/lib $(SRC_ARCH)/lib + @rm -f $(SRC_ARCH)/arch/mach + @rm -rf $(SRC_ARCH)/arch + @ln -sfn $(SARCH) $(SRC_ARCH)/arch +ifdef CONFIG_ETRAX_ARCH_V32 + @ln -sfn ../$(SARCH)/$(MACH) $(SRC_ARCH)/arch/mach +endif + @rm -rf $(SRC_ARCH)/kernel/vmlinux.lds.S + @ln -sfn ../$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S + @rm -rf $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn ../$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c @touch $@ # Create link to sub arch includes $(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) - @echo ' Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink' - @rm -f include/asm-$(ARCH)/arch - @ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch + @echo ' SYMLINK include/asm-$(ARCH)/arch -> include/asm-$(ARCH)/$(SARCH)' + @rm -f $(srctree)/include/asm-$(ARCH)/arch/mach + @rm -f $(srctree)/include/asm-$(ARCH)/arch + @ln -sf $(SARCH) $(srctree)/include/asm-$(ARCH)/arch +ifdef CONFIG_ETRAX_ARCH_V32 + @ln -sf $(MACH) $(srctree)/include/asm-$(ARCH)/arch/mach +endif @touch $@ + +archclean: + $(Q)if [ -e arch/$(ARCH)/boot ]; then \ + $(MAKE) $(clean)=arch/$(ARCH)/boot; \ + fi + +CLEAN_FILES += \ + $(MACHINE)/boot/zImage \ + $(MACHINE)/boot/compressed/decompress.bin \ + $(MACHINE)/boot/compressed/piggy.gz \ + $(MACHINE)/boot/rescue/rescue.bin \ + $(SRC_ARCH)/.links \ + $(srctree)/include/asm-$(ARCH)/.arch + +MRPROPER_FILES += \ + $(SRC_ARCH)/drivers \ + $(SRC_ARCH)/boot \ + $(SRC_ARCH)/lib \ + $(SRC_ARCH)/arch \ + $(SRC_ARCH)/kernel/vmlinux.lds.S \ + $(SRC_ARCH)/kernel/asm-offsets.c + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' + echo '* Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' +endef diff --git a/arch/cris/arch-v10/Kconfig b/arch/cris/arch-v10/Kconfig index 1d61faec77c..adc164e9933 100644 --- a/arch/cris/arch-v10/Kconfig +++ b/arch/cris/arch-v10/Kconfig @@ -1,5 +1,7 @@ if ETRAX_ARCH_V10 +menu "CRIS v10 options" + # ETRAX 100LX v1 has a MMU "feature" requiring a low mapping config CRIS_LOW_MAP bool @@ -228,69 +230,6 @@ config ETRAX_LED12R For products with only one or two controllable LEDs, set this to same as CONFIG_ETRAX_LED1G (normally 2). -choice - prompt "Product debug-port" - depends on ETRAX_ARCH_V10 - default ETRAX_DEBUG_PORT0 - -config ETRAX_DEBUG_PORT0 - bool "Serial-0" - help - Choose a serial port for the ETRAX debug console. Default to - port 0. - -config ETRAX_DEBUG_PORT1 - bool "Serial-1" - help - Use serial port 1 for the console. - -config ETRAX_DEBUG_PORT2 - bool "Serial-2" - help - Use serial port 2 for the console. - -config ETRAX_DEBUG_PORT3 - bool "Serial-3" - help - Use serial port 3 for the console. - -config ETRAX_DEBUG_PORT_NULL - bool "disabled" - help - Disable serial-port debugging. - -endchoice - -choice - prompt "Kernel GDB port" - depends on ETRAX_KGDB - default ETRAX_KGDB_PORT0 - help - Choose a serial port for kernel debugging. NOTE: This port should - not be enabled under Drivers for built-in interfaces (as it has its - own initialization code) and should not be the same as the debug port. - -config ETRAX_KGDB_PORT0 - bool "Serial-0" - help - Use serial port 0 for kernel debugging. - -config ETRAX_KGDB_PORT1 - bool "Serial-1" - help - Use serial port 1 for kernel debugging. - -config ETRAX_KGDB_PORT2 - bool "Serial-2" - help - Use serial port 2 for kernel debugging. - -config ETRAX_KGDB_PORT3 - bool "Serial-3" - help - Use serial port 3 for kernel debugging. - -endchoice choice prompt "Product rescue-port" @@ -454,4 +393,6 @@ config ETRAX_POWERBUTTON_BIT help Configure where power button is connected. +endmenu + endif diff --git a/arch/cris/arch-v10/boot/Makefile b/arch/cris/arch-v10/boot/Makefile index e5b10585110..20c83a53caf 100644 --- a/arch/cris/arch-v10/boot/Makefile +++ b/arch/cris/arch-v10/boot/Makefile @@ -1,13 +1,21 @@ # -# arch/cris/boot/Makefile +# arch/cris/arch-v10/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss -compressed/vmlinuz: - @$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin + +$(obj)/zImage: $(obj)/compressed/vmlinux + @cp $< $@ + @echo ' Kernel: $@ is ready' diff --git a/arch/cris/arch-v10/boot/compressed/Makefile b/arch/cris/arch-v10/boot/compressed/Makefile index 6584a44820f..4a031cb27eb 100644 --- a/arch/cris/arch-v10/boot/compressed/Makefile +++ b/arch/cris/arch-v10/boot/compressed/Makefile @@ -1,45 +1,35 @@ # -# create a compressed vmlinuz image from the binary vmlinux.bin file +# arch/cris/arch-v10/boot/compressed/Makefile # -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -melf $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 LD = ld-cris +ldflags-y += -T $(obj)/decompress.ld +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o -# files to compress -SYSTEM = $(objtree)/vmlinux.bin +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -all: $(target_compressed_dir)/vmlinuz +targets := vmlinux piggy.gz decompress.o decompress.bin -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -# Create vmlinuz image in top-level build directory -$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - @echo " COMPR vmlinux.bin --> vmlinuz" - @cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz - @rm -f piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ +$(obj)/head.o: $(obj)/head.S .config + @$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ -$(target)/misc.o: $(src)/misc.c - $(CC) -D__KERNEL__ -c $< -o $@ +$(obj)/misc.o: $(obj)/misc.c .config + @$(CC) -D__KERNEL__ -c $< -o $@ -# gzip the kernel image +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) -piggy.img: $(SYSTEM) - @cat $(SYSTEM) | gzip -f -9 > piggy.img - -$(target): - mkdir -p $(target) - -clean: - rm -f piggy.img $(objtree)/vmlinuz +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff --git a/arch/cris/arch-v10/boot/compressed/misc.c b/arch/cris/arch-v10/boot/compressed/misc.c index e205d2e7e08..9a43ab19391 100644 --- a/arch/cris/arch-v10/boot/compressed/misc.c +++ b/arch/cris/arch-v10/boot/compressed/misc.c @@ -1,15 +1,13 @@ /* * misc.c * - * $Id: misc.c,v 1.6 2003/10/27 08:04:31 starvik Exp $ - * - * This is a collection of several routines from gzip-1.0.3 + * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 * puts by Nick Holloway 1993, better puts by Martin Mares 1995 * adaptation for Linux/CRIS Axis Communications AB, 1999 - * + * */ /* where the piggybacked kernel image expects itself to live. diff --git a/arch/cris/arch-v10/boot/rescue/Makefile b/arch/cris/arch-v10/boot/rescue/Makefile index 8be9b313031..2e5045b9e19 100644 --- a/arch/cris/arch-v10/boot/rescue/Makefile +++ b/arch/cris/arch-v10/boot/rescue/Makefile @@ -1,56 +1,38 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 +asflags-y += -traditional LD = gcc-cris -mlinux -nostdlib +ldflags-y += -T $(obj)/rescue.ld OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-$(CONFIG_ETRAX_AXISFLASHMAP) = head.o +OBJECT := $(obj)/head.o -all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin +targets := rescue.o rescue.bin -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin -# Place a copy in top-level build directory - cp -p $(target)/rescue.bin $(objtree) +$(obj)/rescue.o: $(OBJECT) FORCE + $(call if_changed,ld) -$(target)/testrescue.bin: $(target) $(target)/testrescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) + +$(obj)/testrescue.bin: $(obj)/testrescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin # Pad it to 784 bytes dd if=/dev/zero of=tmp2423 bs=1 count=784 cat tr.bin tmp2423 >testrescue_tmp.bin - dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784 + dd if=testrescue_tmp.bin of=$(obj)/testrescue.bin bs=1 count=784 rm tr.bin tmp2423 testrescue_tmp.bin -$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin +$(obj)/kimagerescue.bin: $(obj)/kimagerescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/kimagerescue.o ktr.bin # Pad it to 784 bytes, that's what the rescue loader expects dd if=/dev/zero of=tmp2423 bs=1 count=784 cat ktr.bin tmp2423 >kimagerescue_tmp.bin - dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784 + dd if=kimagerescue_tmp.bin of=$(obj)/kimagerescue.bin bs=1 count=784 rm ktr.bin tmp2423 kimagerescue_tmp.bin - -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/testrescue.o: $(src)/testrescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/kimagerescue.o: $(src)/kimagerescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: diff --git a/arch/cris/arch-v10/boot/rescue/head.S b/arch/cris/arch-v10/boot/rescue/head.S index f223cc0c00b..6ba7be8ac4a 100644 --- a/arch/cris/arch-v10/boot/rescue/head.S +++ b/arch/cris/arch-v10/boot/rescue/head.S @@ -1,5 +1,4 @@ -/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $ - * +/* * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition * table at the first sector after the rescue sector. @@ -23,20 +22,20 @@ * Partition table format: * * Code transparency: - * + * * 2 bytes [opcode 'nop'] * 2 bytes [opcode 'di'] * 4 bytes [opcode 'ba <offset>', 8-bit or 16-bit version] * 2 bytes [opcode 'nop', delay slot] * - * Table validation (at +10): - * + * Table validation (at +10): + * * 2 bytes [magic/version word for partitiontable - 0xef, 0xbe] * 2 bytes [length of all entries plus the end marker] * 4 bytes [checksum for the partitiontable itself] * - * Entries, each with the following format, last has offset -1: - * + * Entries, each with the following format, last has offset -1: + * * 4 bytes [offset in bytes, from start of flash] * 4 bytes [length in bytes of partition] * 4 bytes [checksum, simple longword sum] @@ -47,9 +46,9 @@ * End marker * * 4 bytes [-1] - * + * * 10 bytes [0, padding] - * + * * Bit 0 in flags signifies RW or RO. The rescue code only bothers * to check the checksum for RO partitions, since the others will * change their data without updating the checksums. A 1 in bit 0 @@ -59,26 +58,29 @@ * * During the wait for serial input, the status LED will flash so the * user knows something went wrong. - * - * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * + * Copyright (C) 1999-2007 Axis Communications AB */ +#ifdef CONFIG_ETRAX_AXISFLASHMAP + #define ASSEMBLER_MACROS_ONLY #include <asm/arch/sv_addr_ag.h> ;; The partitiontable is looked for at the first sector after the boot ;; sector. Sector size is 65536 bytes in all flashes we use. - + #define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR #define PTABLE_MAGIC 0xbeef ;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0. - ;; That is not where we put our downloaded serial boot-code. The length is - ;; enough for downloading code that loads the rest of itself (after - ;; having setup the DRAM etc). It is the same length as the on-chip - ;; ROM loads, so the same host loader can be used to load a rescued - ;; product as well as one booted through the Etrax serial boot code. - + ;; That is not where we put our downloaded serial boot-code. + ;; The length is enough for downloading code that loads the rest + ;; of itself (after having setup the DRAM etc). + ;; It is the same length as the on-chip ROM loads, so the same + ;; host loader can be used to load a rescued product as well as + ;; one booted through the Etrax serial boot code. + #define CODE_START 0x40000000 #define CODE_LENGTH 784 @@ -102,7 +104,7 @@ #define SERRECC R_SERIAL2_REC_CTRL #define SERRDAT R_SERIAL2_REC_DATA #define SERSTAT R_SERIAL2_STATUS -#endif +#endif #ifdef CONFIG_ETRAX_RESCUE_SER3 #define SERXOFF R_SERIAL3_XOFF #define SERBAUD R_SERIAL3_BAUD @@ -115,60 +117,61 @@ #define RAM_INIT_MAGIC 0x56902387 .text - + ;; This is the entry point of the rescue code ;; 0x80000000 if loaded in flash (as it should be) - ;; since etrax actually starts at address 2 when booting from flash, we + ;; Since etrax actually starts at address 2 when booting from flash, we ;; put a nop (2 bytes) here first so we dont accidentally skip the di - nop + nop di jump in_cache ; enter cached area instead in_cache: - ;; first put a jump test to give a possibility of upgrading the rescue code - ;; without erasing/reflashing the sector. we put a longword of -1 here and if - ;; it is not -1, we jump using the value as jump target. since we can always - ;; change 1's to 0's without erasing the sector, it is possible to add new + ;; First put a jump test to give a possibility of upgrading the + ;; rescue code without erasing/reflashing the sector. + ;; We put a longword of -1 here and if it is not -1, we jump using + ;; the value as jump target. Since we can always change 1's to 0's + ;; without erasing the sector, it is possible to add new ;; code after this and altering the jumptarget in an upgrade. jtcd: move.d [jumptarget], $r0 cmp.d 0xffffffff, $r0 beq no_newjump nop - + jump [$r0] -jumptarget: +jumptarget: .dword 0xffffffff ; can be overwritten later to insert new code - + no_newjump: -#ifdef CONFIG_ETRAX_ETHERNET +#ifdef CONFIG_ETRAX_ETHERNET ;; Start MII clock to make sure it is running when tranceiver is reset move.d 0x3, $r0 ; enable = on, phy = mii_clk move.d $r0, [R_NETWORK_GEN_CONFIG] #endif - + ;; We need to setup the bus registers before we start using the DRAM #include "../../lib/dram_init.S" ;; we now should go through the checksum-table and check the listed ;; partitions for errors. - + move.d PTABLE_START, $r3 move.d [$r3], $r0 cmp.d NOP_DI, $r0 ; make sure the nop/di is there... bne do_rescue nop - + ;; skip the code transparency block (10 bytes). addq 10, $r3 - + ;; check for correct magic - + move.w [$r3+], $r0 cmp.w PTABLE_MAGIC, $r0 bne do_rescue ; didn't recognize - trig rescue @@ -186,11 +189,11 @@ no_newjump: cmp.d $r0, $r4 bne do_rescue ; didn't match - trig rescue nop - + ;; ptable is ok. validate each entry. moveq -1, $r7 - + ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) bne notfirst ; check if it is the partition containing ptable nop ; yes.. @@ -199,7 +202,7 @@ ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) sub.d $r8, $r2 ; minus the ptable length ba bosse nop -notfirst: +notfirst: cmp.d -1, $r1 ; the end of the ptable ? beq flash_ok ; if so, the flash is validated move.d [$r3+], $r2 ; partition length @@ -213,47 +216,46 @@ bosse: move.d [$r3+], $r5 ; checksum bpl 1f nop move.d $r1, $r7 ; remember boot partition offset -1: - +1: add.d PTABLE_START, $r1 - + jsr checksum ; checksum the partition - + cmp.d $r0, $r5 beq ploop ; checksums matched, go to next entry nop ;; otherwise fall through to the rescue code. - + do_rescue: ;; setup port PA and PB default initial directions and data ;; (so we can flash LEDs, and so that DTR and others are set) - + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 move.b $r0, [R_PORT_PA_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 move.b $r0, [R_PORT_PA_DATA] - + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 move.b $r0, [R_PORT_PB_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 move.b $r0, [R_PORT_PB_DATA] ;; setup the serial port at 115200 baud - + moveq 0, $r0 - move.d $r0, [SERXOFF] + move.d $r0, [SERXOFF] move.b 0x99, $r0 - move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive - move.b 0x40, $r0 ; rec enable - move.b $r0, [SERRECC] + move.b 0x40, $r0 ; rec enable + move.b $r0, [SERRECC] moveq 0, $r1 ; "timer" to clock out a LED red flash move.d CODE_START, $r3 ; destination counter movu.w CODE_LENGTH, $r4; length - + wait_ser: addq 1, $r1 #ifndef CONFIG_ETRAX_NO_LEDS @@ -272,20 +274,20 @@ wait_ser: nop 1: not $r0 ; clear bit and.d $r0, $r2 -2: +2: #ifdef CONFIG_ETRAX_PA_LEDS - move.b $r2, [R_PORT_PA_DATA] -#endif + move.b $r2, [R_PORT_PA_DATA] +#endif #ifdef CONFIG_ETRAX_PB_LEDS - move.b $r2, [R_PORT_PB_DATA] + move.b $r2, [R_PORT_PB_DATA] #endif #ifdef CONFIG_ETRAX_90000000_LEDS move.b $r2, [0x90000000] #endif #endif - + ;; check if we got something on the serial port - + move.b [SERSTAT], $r0 btstq 0, $r0 ; data_avail bpl wait_ser @@ -295,14 +297,15 @@ wait_ser: move.b [SERRDAT], $r0 move.b $r0, [$r3+] - + subq 1, $r4 ; decrease length bne wait_ser nop ;; jump into downloaded code - move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is + ; initialized jump CODE_START flash_ok: @@ -313,7 +316,8 @@ flash_ok: nop move.d PTABLE_START, $r7; otherwise use the ptable start 1: - move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is + ; initialized jump $r7 ; boot! @@ -327,7 +331,8 @@ checksum: moveq 0, $r0 moveq CONFIG_ETRAX_FLASH1_SIZE, $r6 - ;; If the first physical flash memory is exceeded wrap to the second one. + ;; If the first physical flash memory is exceeded wrap to the + ;; second one btstq 26, $r1 ; Are we addressing first flash? bpl 1f nop @@ -351,3 +356,5 @@ checksum: 3: move.d MEM_CSE1_START, $r1 ; wrap to second flash ba 2b nop + +#endif diff --git a/arch/cris/arch-v10/boot/rescue/kimagerescue.S b/arch/cris/arch-v10/boot/rescue/kimagerescue.S index cbccd6316d3..55eeff8bb08 100644 --- a/arch/cris/arch-v10/boot/rescue/kimagerescue.S +++ b/arch/cris/arch-v10/boot/rescue/kimagerescue.S @@ -1,5 +1,4 @@ -/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ - * +/* * Rescue code to be prepended on a kimage and copied to the * rescue serial port. * This is called from the rescue code, it will copy received data to @@ -7,13 +6,13 @@ */ #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> #define CODE_START 0x40004000 #define CODE_LENGTH 784 #define TIMEOUT_VALUE 1000 - - + + #ifdef CONFIG_ETRAX_RESCUE_SER0 #define SERXOFF R_SERIAL0_XOFF #define SERBAUD R_SERIAL0_BAUD @@ -34,7 +33,7 @@ #define SERRECC R_SERIAL2_REC_CTRL #define SERRDAT R_SERIAL2_REC_DATA #define SERSTAT R_SERIAL2_STATUS -#endif +#endif #ifdef CONFIG_ETRAX_RESCUE_SER3 #define SERXOFF R_SERIAL3_XOFF #define SERBAUD R_SERIAL3_BAUD @@ -48,54 +47,55 @@ ;; 0x80000000 if loaded in flash (as it should be) ;; since etrax actually starts at address 2 when booting from flash, we ;; put a nop (2 bytes) here first so we dont accidentally skip the di - - nop + + nop di -#ifndef CONFIG_SVINTO_SIM +#ifndef CONFIG_SVINTO_SIM ;; setup port PA and PB default initial directions and data ;; (so we can flash LEDs, and so that DTR and others are set) - + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 move.b $r0, [R_PORT_PA_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 move.b $r0, [R_PORT_PA_DATA] - + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 move.b $r0, [R_PORT_PB_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 move.b $r0, [R_PORT_PB_DATA] - + ;; We need to setup the bus registers before we start using the DRAM #include "../../lib/dram_init.S" - + #endif ;; Setup the stack to a suitably high address. ;; We assume 8 MB is the minimum DRAM in an eLinux ;; product and put the sp at the top for now. move.d 0x40800000, $sp - + ;; setup the serial port at 115200 baud - + moveq 0, $r0 - move.d $r0, [SERXOFF] + move.d $r0, [SERXOFF] move.b 0x99, $r0 - move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit + ; and receive move.b 0x40, $r0 ; rec enable - move.b $r0, [SERRECC] + move.b $r0, [SERRECC] - moveq 0, $r1 ; "timer" to clock out a LED red flash - move.d CODE_START, $r3 ; destination counter + moveq 0, $r1 ; "timer" to clock out a LED red flash + move.d CODE_START, $r3 ; destination counter move.d CODE_LENGTH, $r4 ; length move.d TIMEOUT_VALUE, $r5 ; "timeout" until jump wait_ser: addq 1, $r1 - subq 1, $r5 ; decrease timeout - beq jump_start ; timed out + subq 1, $r5 ; decrease timeout + beq jump_start ; timed out nop #ifndef CONFIG_ETRAX_NO_LEDS #ifdef CONFIG_ETRAX_PA_LEDS @@ -111,21 +111,21 @@ wait_ser: or.d $r0, $r2 ; set bit ba 2f nop -1: not $r0 ; clear bit +1: not $r0 ; clear bit and.d $r0, $r2 -2: +2: #ifdef CONFIG_ETRAX_PA_LEDS move.b $r2, [R_PORT_PA_DATA] -#endif +#endif #ifdef CONFIG_ETRAX_PB_LEDS move.b $r2, [R_PORT_PB_DATA] #endif #endif - + ;; check if we got something on the serial port - + move.b [SERSTAT], $r0 - btstq 0, $r0 ; data_avail + btstq 0, $r0 ; data_avail bpl wait_ser nop @@ -134,7 +134,7 @@ wait_ser: move.b [SERRDAT], $r0 move.b $r0, [$r3+] move.d TIMEOUT_VALUE, $r5 ; reset "timeout" - subq 1, $r4 ; decrease length + subq 1, $r4 ; decrease length bne wait_ser nop jump_start: diff --git a/arch/cris/arch-v10/boot/rescue/testrescue.S b/arch/cris/arch-v10/boot/rescue/testrescue.S index 566a9f34125..2d937f9afe2 100644 --- a/arch/cris/arch-v10/boot/rescue/testrescue.S +++ b/arch/cris/arch-v10/boot/rescue/testrescue.S @@ -1,13 +1,12 @@ -/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ - * +/* * Simple testcode to download by the rescue block. - * Just lits some LEDs to show it was downloaded correctly. - * + * Just lights some LEDs to show it was downloaded correctly. + * * Copyright (C) 1999 Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> .text @@ -16,11 +15,10 @@ moveq -1, $r2 move.b $r2, [R_PORT_PA_DIR] moveq 0, $r2 - move.b $r2, [R_PORT_PA_DATA] + move.b $r2, [R_PORT_PA_DATA] endless: nop ba endless nop - diff --git a/arch/cris/arch-v10/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig index 96740ef497d..58f5864a668 100644 --- a/arch/cris/arch-v10/drivers/Kconfig +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -9,37 +9,6 @@ config ETRAX_ETHERNET This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. -choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -endchoice - config ETRAX_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V10 @@ -84,32 +53,6 @@ config ETRAX_SERIAL_PORT0 the same DMA channels. choice - prompt "Ser0 DMA out assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA6_OUT - -config ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "DMA 6" - -endchoice - -choice - prompt "Ser0 DMA in assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA7_IN - -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT0_DMA7_IN - bool "DMA 7" - -endchoice - -choice prompt "Ser0 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT0 default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE @@ -198,32 +141,6 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX 100 serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA out assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA8_OUT - -config ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT1_DMA8_OUT - bool "DMA 8" - -endchoice - -choice - prompt "Ser1 DMA in assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA9_IN - -config ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT1_DMA9_IN - bool "DMA 9" - -endchoice - -choice prompt "Ser1 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT1 default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE @@ -315,32 +232,6 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX 100 serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA out assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA2_OUT - -config ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "DMA 2" - -endchoice - -choice - prompt "Ser2 DMA in assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA3_IN - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT2_DMA3_IN - bool "DMA 3" - -endchoice - -choice prompt "Ser2 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT2 default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE @@ -429,32 +320,6 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX 100 serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA out assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA4_OUT - -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT3_DMA4_OUT - bool "DMA 4" - -endchoice - -choice - prompt "Ser3 DMA in assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA5_IN - -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT3_DMA5_IN - bool "DMA 5" - -endchoice - -choice prompt "Ser3 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT3 default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE @@ -563,21 +428,6 @@ config ETRAX_USB_HOST_PORT2 depends on ETRAX_USB_HOST default n -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V10 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. - config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -731,37 +581,6 @@ config ETRAX_PB_CHANGEABLE_BITS Bit set = changeable. You probably want 00 here. -config ETRAX_RTC - bool "Real Time Clock support" - depends on ETRAX_ARCH_V10 - help - Enables drivers for the Real-Time Clock battery-backed chips on - some products. The kernel reads the time when booting, and - the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a - rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc - device, major 121. You can check the time with cat /proc/rtc, but - normal time reading should be done using libc function time and - friends. - -choice - prompt "RTC chip" - depends on ETRAX_RTC - default ETRAX_DS1302 - -config ETRAX_DS1302 - bool "DS1302" - help - Enables the driver for the DS1302 Real-Time Clock battery-backed - chip on some products. - -config ETRAX_PCF8563 - bool "PCF8563" - help - Enables the driver for the PCF8563 Real-Time Clock battery-backed - chip on some products. - -endchoice - config ETRAX_DS1302_RST_ON_GENERIC_PORT bool "DS1302 RST on Generic Port" depends on ETRAX_DS1302 diff --git a/arch/cris/arch-v10/drivers/Makefile b/arch/cris/arch-v10/drivers/Makefile index 20258e36f38..44bf2e88c26 100644 --- a/arch/cris/arch-v10/drivers/Makefile +++ b/arch/cris/arch-v10/drivers/Makefile @@ -2,11 +2,11 @@ # Makefile for Etrax-specific drivers # -obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o -obj-$(CONFIG_ETRAX_I2C) += i2c.o -obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o -obj-$(CONFIG_ETRAX_GPIO) += gpio.o -obj-$(CONFIG_ETRAX_DS1302) += ds1302.o +obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o +obj-$(CONFIG_ETRAX_I2C) += i2c.o +obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAX_DS1302) += ds1302.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o - +obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o diff --git a/arch/cris/arch-v10/drivers/axisflashmap.c b/arch/cris/arch-v10/drivers/axisflashmap.c index ea3cf2e39a1..b3bdda93ffe 100644 --- a/arch/cris/arch-v10/drivers/axisflashmap.c +++ b/arch/cris/arch-v10/drivers/axisflashmap.c @@ -10,129 +10,6 @@ * tells us what other partitions to define. If there isn't, we use a default * partition split defined below. * - * $Log: axisflashmap.c,v $ - * Revision 1.11 2004/11/15 10:27:14 starvik - * Corrected typo (Thanks to Milton Miller <miltonm@bga.com>). - * - * Revision 1.10 2004/08/16 12:37:22 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.6 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.4 2002/11/20 11:56:10 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.3 2002/11/13 14:54:13 starvik - * Copied from linux 2.4 - * - * Revision 1.28 2002/10/01 08:08:43 jonashg - * The first partition ends at the start of the partition table. - * - * Revision 1.27 2002/08/21 09:23:13 jonashg - * Speling. - * - * Revision 1.26 2002/08/21 08:35:20 jonashg - * Cosmetic change to printouts. - * - * Revision 1.25 2002/08/21 08:15:42 jonashg - * Made it compile even without CONFIG_MTD_CONCAT defined. - * - * Revision 1.24 2002/08/20 13:12:35 jonashg - * * New approach to probing. Probe cse0 and cse1 separately and (mtd)concat - * the results. - * * Removed compile time tests concerning how the mtdram driver has been - * configured. The user will know about the misconfiguration at runtime - * instead. (The old approach made it impossible to use mtdram for anything - * else than RAM boot). - * - * Revision 1.23 2002/05/13 12:12:28 johana - * Allow compile without CONFIG_MTD_MTDRAM but warn at compiletime and - * be informative at runtime. - * - * Revision 1.22 2002/05/13 10:24:44 johana - * Added #if checks on MTDRAM CONFIG - * - * Revision 1.21 2002/05/06 16:05:20 johana - * Removed debug printout. - * - * Revision 1.20 2002/05/06 16:03:00 johana - * No more cramfs as root hack in generic code. - * It's handled by axisflashmap using mtdram. - * - * Revision 1.19 2002/03/15 17:10:28 bjornw - * Changed comment about cached access since we changed this before - * - * Revision 1.18 2002/03/05 17:06:15 jonashg - * Try amd_flash probe before cfi_probe since amd_flash driver can handle two - * (or more) flash chips of different model and the cfi driver cannot. - * - * Revision 1.17 2001/11/12 19:42:38 pkj - * Fixed compiler warnings. - * - * Revision 1.16 2001/11/08 11:18:58 jonashg - * Always read from uncached address to avoid problems with flushing - * cachelines after write and MTD-erase. No performance loss have been - * seen yet. - * - * Revision 1.15 2001/10/19 12:41:04 jonashg - * Name of probe has changed in MTD. - * - * Revision 1.14 2001/09/21 07:14:10 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.13 2001/08/15 13:57:35 jonashg - * Entire MTD updated to the linux 2.4.7 version. - * - * Revision 1.12 2001/06/11 09:50:30 jonashg - * Oops, 2MB is 0x200000 bytes. - * - * Revision 1.11 2001/06/08 11:39:44 jonashg - * Changed sizes and offsets in axis_default_partitions to use - * CONFIG_ETRAX_PTABLE_SECTOR. - * - * Revision 1.10 2001/05/29 09:42:03 jonashg - * Use macro for end marker length instead of sizeof. - * - * Revision 1.9 2001/05/29 08:52:52 jonashg - * Gave names to the magic fours (size of the ptable end marker). - * - * Revision 1.8 2001/05/28 15:36:20 jonashg - * * Removed old comment about ptable location in flash (it's a CONFIG_ option). - * * Variable ptable was initialized twice to the same value. - * - * Revision 1.7 2001/04/05 13:41:46 markusl - * Updated according to review remarks - * - * Revision 1.6 2001/03/07 09:21:21 bjornw - * No need to waste .data - * - * Revision 1.5 2001/03/06 16:27:01 jonashg - * Probe the entire flash area for flash devices. - * - * Revision 1.4 2001/02/23 12:47:15 bjornw - * Uncached flash in LOW_MAP moved from 0xe to 0x8 - * - * Revision 1.3 2001/02/16 12:11:45 jonashg - * MTD driver amd_flash is now included in MTD CVS repository. - * (It's now in drivers/mtd). - * - * Revision 1.2 2001/02/09 11:12:22 jonashg - * Support for AMD compatible non-CFI flash chips. - * Only tested with Toshiba TC58FVT160 so far. - * - * Revision 1.1 2001/01/12 17:01:18 bjornw - * * Added axisflashmap.c, a physical mapping for MTD that reads and understands - * Axis partition-table format. - * - * */ #include <linux/module.h> @@ -235,7 +112,7 @@ static struct map_info map_cse1 = { }; /* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 +#define MAX_PARTITIONS 7 #define NUM_DEFAULT_PARTITIONS 3 /* @@ -300,6 +177,15 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -316,15 +202,14 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE - if (!mtd_cs) { + if (!mtd_cs) mtd_cs = do_map_probe("jedec_probe", map_cs); - } #endif return mtd_cs; } -/* +/* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct * so that MTD partitions can cross chip boundries. @@ -351,7 +236,7 @@ static struct mtd_info *flash_probe(void) if (mtd_cse0 && mtd_cse1) { #ifdef CONFIG_MTD_CONCAT struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 }; - + /* Since the concatenation layer adds a small overhead we * could try to figure out if the chips in cse0 and cse1 are * identical and reprobe the whole cse0+cse1 window. But since @@ -372,7 +257,7 @@ static struct mtd_info *flash_probe(void) /* The best we can do now is to only use what we found * at cse0. - */ + */ mtd_cse = mtd_cse0; map_destroy(mtd_cse1); } @@ -395,7 +280,7 @@ static int __init init_axis_flash(void) struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + const char pmsg[] = " /dev/flash%d at 0x%08x, size 0x%08x\n"; if (!(mymtd = flash_probe())) { /* There's no reason to use this module if no flash chip can @@ -435,7 +320,7 @@ static int __init init_axis_flash(void) unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; unsigned char *p; unsigned long csum = 0; - + ptable = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head)); @@ -490,6 +375,16 @@ static int __init init_axis_flash(void) pidx++; } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (mymtd) { + main_partition.size = mymtd->size; + err = add_mtd_partitions(mymtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); + } +#endif + if (mymtd) { if (use_default_ptable) { printk(KERN_INFO " Using default partition table.\n"); @@ -499,9 +394,8 @@ static int __init init_axis_flash(void) err = add_mtd_partitions(mymtd, axis_partitions, pidx); } - if (err) { + if (err) panic("axisflashmap could not add MTD partitions!\n"); - } } if (!romfs_in_flash) { @@ -515,25 +409,24 @@ static int __init init_axis_flash(void) #else struct mtd_info *mtd_ram; - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) panic("axisflashmap couldn't allocate memory for " "mtd_info!\n"); - } printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); - - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { + printk(pmsg, pidx, (unsigned)romfs_start, + (unsigned)romfs_length); + + err = mtdram_init_device(mtd_ram, + (void *)romfs_start, + romfs_length, + "romfs"); + if (err) panic("axisflashmap could not initialize MTD RAM " "device!\n"); - } #endif } - return err; } diff --git a/arch/cris/arch-v10/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c index 1d1936a1813..c9aa3904be0 100644 --- a/arch/cris/arch-v10/drivers/ds1302.c +++ b/arch/cris/arch-v10/drivers/ds1302.c @@ -333,7 +333,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); return 0; } - case RTC_VLOW_RD: + case RTC_VL_READ: { /* TODO: * Implement voltage low detection support @@ -342,7 +342,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, " is not supported\n"); return 0; } - case RTC_VLOW_SET: + case RTC_VL_CLR: { /* TODO: * Nothing to do since Voltage Low detection is not supported diff --git a/arch/cris/arch-v10/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c index be35a70798a..f1cac9dc75b 100644 --- a/arch/cris/arch-v10/drivers/eeprom.c +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -19,77 +19,6 @@ *! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted *! in the spin-lock. *! -*! $Log: eeprom.c,v $ -*! Revision 1.12 2005/06/19 17:06:46 starvik -*! Merge of Linux 2.6.12. -*! -*! Revision 1.11 2005/01/26 07:14:46 starvik -*! Applied diff from kernel janitors (Nish Aravamudan). -*! -*! Revision 1.10 2003/09/11 07:29:48 starvik -*! Merge of Linux 2.6.0-test5 -*! -*! Revision 1.9 2003/07/04 08:27:37 starvik -*! Merge of Linux 2.5.74 -*! -*! Revision 1.8 2003/04/09 05:20:47 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.6 2003/02/10 07:19:28 starvik -*! Removed misplaced ; -*! -*! Revision 1.5 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.4 2002/11/20 11:56:10 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.3 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.8 2001/06/15 13:24:29 jonashg -*! * Added verification of pointers from userspace in read and write. -*! * Made busy counter volatile. -*! * Added define for initial write delay. -*! * Removed warnings by using loff_t instead of unsigned long. -*! -*! Revision 1.7 2001/06/14 15:26:54 jonashg -*! Removed test because condition is always true. -*! -*! Revision 1.6 2001/06/14 15:18:20 jonashg -*! Kb -> kB (makes quite a difference if you don't know if you have 2k or 16k). -*! -*! Revision 1.5 2001/06/14 14:39:51 jonashg -*! Forgot to use name when registering the driver. -*! -*! Revision 1.4 2001/06/14 14:35:47 jonashg -*! * Gave driver a name and used it in printk's. -*! * Cleanup. -*! -*! Revision 1.3 2001/03/19 16:04:46 markusl -*! Fixed init of fops struct -*! -*! Revision 1.2 2001/03/19 10:35:07 markusl -*! 2.4 port of eeprom driver -*! -*! Revision 1.8 2000/05/18 10:42:25 edgar -*! Make sure to end write cycle on _every_ write -*! -*! Revision 1.7 2000/01/17 17:41:01 johana -*! Adjusted probing and return -ENOSPC when writing outside EEPROM -*! -*! Revision 1.6 2000/01/17 15:50:36 johana -*! Added adaptive timing adjustments and fixed autoprobing for 2k and 16k(?) -*! EEPROMs -*! -*! Revision 1.5 1999/09/03 15:07:37 edgar -*! Added bail-out check to the spinlock -*! -*! Revision 1.4 1999/09/03 12:11:17 bjornw -*! Proper atomicity (need to use spinlocks, not if's). users -> busy. -*! -*! *! (c) 1999 Axis Communications AB, Lund, Sweden *!*****************************************************************************/ @@ -103,10 +32,10 @@ #include <asm/uaccess.h> #include "i2c.h" -#define D(x) +#define D(x) /* If we should use adaptive timing or not: */ -//#define EEPROM_ADAPTIVE_TIMING +/* #define EEPROM_ADAPTIVE_TIMING */ #define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */ #define EEPROM_MINOR_NR 0 diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c index 0d347a70583..68a998bd106 100644 --- a/arch/cris/arch-v10/drivers/gpio.c +++ b/arch/cris/arch-v10/drivers/gpio.c @@ -1,138 +1,11 @@ -/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ - * +/* * Etrax general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB + * Copyright (c) 1999-2007 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G) - * - * $Log: gpio.c,v $ - * Revision 1.17 2005/06/19 17:06:46 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.16 2005/03/07 13:02:29 starvik - * Protect driver global states with spinlock - * - * Revision 1.15 2005/01/05 06:08:55 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.14 2004/12/13 12:21:52 starvik - * Added I/O and DMA allocators from Linux 2.4 - * - * Revision 1.12 2004/08/24 07:19:59 starvik - * Whitespace cleanup - * - * Revision 1.11 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.9 2003/09/11 07:29:48 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.8 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.7 2003/01/10 07:44:07 starvik - * init_ioremap is now called by kernel before drivers are initialized - * - * Revision 1.6 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.5 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.4 2002/11/18 10:10:05 starvik - * Linux 2.5 port of latest gpio.c from Linux 2.4 - * - * Revision 1.20 2002/10/16 21:16:24 johana - * Added support for PA high level interrupt. - * That gives 2ms response time with iodtest for high levels and 2-12 ms - * response time on low levels if the check is not made in - * process.c:cpu_idle() as well. - * - * Revision 1.19 2002/10/14 18:27:33 johana - * Implemented alarm handling so select() now works. - * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in - * cpu_idle(). - * Otherwise I get 15-18 ms (same as doing the poll in userspace - - * but less overhead). - * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it - * is in 2.4) as well? - * TODO? Perhaps call request_irq()/free_irq() only when needed? - * Increased version to 2.5 - * - * Revision 1.18 2002/10/11 15:02:00 johana - * Mask inverted 8 bit value in setget_input(). - * - * Revision 1.17 2002/06/17 15:53:01 johana - * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT - * that take a pointer as argument and thus can handle 32 bit ports (G) - * correctly. - * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT. - * (especially if Port G bit 31 is used) - * - * Revision 1.16 2002/06/17 09:59:51 johana - * Returning 32 bit values in the ioctl return value doesn't work if bit - * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF. - * A new set of ioctl's will be added. - * - * Revision 1.15 2002/05/06 13:19:13 johana - * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well. - * - * Revision 1.14 2002/04/12 12:01:53 johana - * Use global r_port_g_data_shadow. - * Moved gpio_init_port_g() closer to gpio_init() and marked it __init. - * - * Revision 1.13 2002/04/10 12:03:55 johana - * Added support for port G /dev/gpiog (minor 3). - * Changed indentation on switch cases. - * Fixed other spaces to tabs. - * - * Revision 1.12 2001/11/12 19:42:15 pkj - * * Corrected return values from gpio_leds_ioctl(). - * * Fixed compiler warnings. - * - * Revision 1.11 2001/10/30 14:39:12 johana - * Added D() around gpio_write printk. - * - * Revision 1.10 2001/10/25 10:24:42 johana - * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast - * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB - * from ~60 seconds to 4 seconds). - * Added save_flags/cli/restore_flags in ioctl. - * - * Revision 1.9 2001/05/04 14:16:07 matsfg - * Corrected spelling error - * - * Revision 1.8 2001/04/27 13:55:26 matsfg - * Moved initioremap. - * Turns off all LEDS on init. - * Added support for shutdown and powerbutton. - * - * Revision 1.7 2001/04/04 13:30:08 matsfg - * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping - * - * Revision 1.6 2001/03/26 16:03:06 bjornw - * Needs linux/config.h - * - * Revision 1.5 2001/03/26 14:22:03 bjornw - * Namechange of some config options - * - * Revision 1.4 2001/02/27 13:52:48 bjornw - * malloc.h -> slab.h - * - * Revision 1.3 2001/01/24 15:06:48 bjornw - * gpio_wq correct type - * - * Revision 1.2 2001/01/18 16:07:30 bjornw - * 2.4 port - * - * Revision 1.1 2001/01/18 15:55:16 bjornw - * Verbatim copy of etraxgpio.c from elinux 2.0 added - * - * */ @@ -165,7 +38,7 @@ static int dp_cnt; #else #define DP(x) #endif - + static char gpio_name[] = "etrax gpio"; #if 0 @@ -173,9 +46,9 @@ static wait_queue_head_t *gpio_wq; #endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off); static int gpio_open(struct inode *inode, struct file *filp); static int gpio_release(struct inode *inode, struct file *filp); static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); @@ -201,22 +74,22 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -static int gpio_some_alarms = 0; /* Set if someone uses alarm */ -static unsigned long gpio_pa_irq_enabled_mask = 0; +static int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_irq_enabled_mask; static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ /* Port A and B use 8 bit access, but Port G is 32 bit */ #define NUM_PORTS (GPIO_MINOR_B+1) -static volatile unsigned char *ports[NUM_PORTS] = { - R_PORT_PA_DATA, +static volatile unsigned char *ports[NUM_PORTS] = { + R_PORT_PA_DATA, R_PORT_PB_DATA, }; static volatile unsigned char *shads[NUM_PORTS] = { - &port_pa_data_shadow, + &port_pa_data_shadow, &port_pb_data_shadow }; @@ -236,29 +109,29 @@ static volatile unsigned char *shads[NUM_PORTS] = { #endif -static unsigned char changeable_dir[NUM_PORTS] = { +static unsigned char changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, - CONFIG_ETRAX_PB_CHANGEABLE_DIR + CONFIG_ETRAX_PB_CHANGEABLE_DIR }; -static unsigned char changeable_bits[NUM_PORTS] = { +static unsigned char changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, - CONFIG_ETRAX_PB_CHANGEABLE_BITS + CONFIG_ETRAX_PB_CHANGEABLE_BITS }; -static volatile unsigned char *dir[NUM_PORTS] = { - R_PORT_PA_DIR, - R_PORT_PB_DIR +static volatile unsigned char *dir[NUM_PORTS] = { + R_PORT_PA_DIR, + R_PORT_PB_DIR }; static volatile unsigned char *dir_shadow[NUM_PORTS] = { - &port_pa_dir_shadow, - &port_pb_dir_shadow + &port_pa_dir_shadow, + &port_pb_dir_shadow }; /* All bits in port g that can change dir. */ static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; -/* Port G is 32 bit, handle it special, some bits are both inputs +/* Port G is 32 bit, handle it special, some bits are both inputs and outputs at the same time, only some of the bits can change direction and some of them in groups of 8 bit. */ static unsigned long changeable_dir_g; @@ -269,18 +142,17 @@ static unsigned long dir_g_shadow; /* 1=output */ #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) - -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; + struct gpio_private *priv = file->private_data; unsigned long data; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { - unsigned long flags; unsigned long tmp; data = *R_PORT_PA_DATA; /* PA has support for high level interrupt - @@ -288,27 +160,25 @@ gpio_poll(struct file *file, */ tmp = ~data & priv->highalarm & 0xFF; tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); - local_irq_save(flags); + gpio_pa_irq_enabled_mask |= tmp; *R_IRQ_MASK1_SET = tmp; - local_irq_restore(flags); - } else if (priv->minor == GPIO_MINOR_B) data = *R_PORT_PB_DATA; else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; else { - spin_unlock(&gpio_lock); - return 0; + mask = 0; + goto out; } - + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } - spin_unlock(&gpio_lock); - +out: + spin_unlock_irqrestore(&gpio_lock, flags); DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); return mask; @@ -316,16 +186,19 @@ gpio_poll(struct file *file, int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; int ret = 0; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + priv = alarmlist; while (priv) { - if (USE_PORTS(priv)) { + if (USE_PORTS(priv)) data = *priv->port; - } else if (priv->minor == GPIO_MINOR_G) { + else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; - } + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); @@ -334,12 +207,12 @@ int etrax_gpio_wake_up_check(void) } priv = priv->next; } - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { if (gpio_some_alarms) { etrax_gpio_wake_up_check(); @@ -349,10 +222,13 @@ gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_interrupt(int irq, void *dev_id) { unsigned long tmp; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + /* Find what PA interrupts are active */ tmp = (*R_IRQ_READ1); @@ -363,75 +239,70 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) *R_IRQ_MASK1_CLR = tmp; gpio_pa_irq_enabled_mask &= ~tmp; - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } + return IRQ_NONE; } +static void gpio_write_bit(struct gpio_private *priv, + unsigned char data, int bit) +{ + *priv->port = *priv->shadow &= ~(priv->clk_mask); + if (data & 1 << bit) + *priv->port = *priv->shadow |= priv->data_mask; + else + *priv->port = *priv->shadow &= ~(priv->data_mask); + + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= priv->clk_mask; +} -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +static void gpio_write_byte(struct gpio_private *priv, unsigned char data) { - struct gpio_private *priv = (struct gpio_private *)file->private_data; - unsigned char data, clk_mask, data_mask, write_msb; - unsigned long flags; + int i; - spin_lock(&gpio_lock); + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(priv, data, i); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(priv, data, i); +} +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; ssize_t retval = count; - if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { - retval = -EFAULT; - goto out; - } - - if (!access_ok(VERIFY_READ, buf, count)) { - retval = -EFAULT; - goto out; - } - clk_mask = priv->clk_mask; - data_mask = priv->data_mask; + + if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gpio_lock, flags); + /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { + if (priv->clk_mask == 0 || priv->data_mask == 0) { retval = -EPERM; goto out; } - write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); - while (count--) { - int i; - data = *buf++; - if (priv->write_msb) { - for (i = 7; i >= 0;i--) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } else { - for (i = 0; i <= 7;i++) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } - } + + D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " + "clk 0x%02X msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + while (count--) + gpio_write_byte(priv, *buf++); + out: - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return retval; } @@ -442,22 +313,20 @@ gpio_open(struct inode *inode, struct file *filp) { struct gpio_private *priv; int p = iminor(inode); + unsigned long flags; if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; if (USE_PORTS(priv)) { /* A and B */ priv->port = ports[p]; priv->shadow = shads[p]; @@ -480,7 +349,13 @@ gpio_open(struct inode *inode, struct file *filp) priv->data_mask = 0; init_waitqueue_head(&priv->alarm_wq); - filp->private_data = (void *)priv; + filp->private_data = priv; + + /* link it into our alarmlist */ + spin_lock_irqsave(&gpio_lock, flags); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } @@ -490,11 +365,12 @@ gpio_release(struct inode *inode, struct file *filp) { struct gpio_private *p; struct gpio_private *todel; + unsigned long flags; - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); - p = alarmlist; - todel = (struct gpio_private *)filp->private_data; + p = alarmlist; + todel = filp->private_data; /* unlink from alarmlist and free the private structure */ @@ -512,123 +388,114 @@ gpio_release(struct inode *inode, struct file *filp) while (p) { if (p->highalarm | p->lowalarm) { gpio_some_alarms = 1; - spin_unlock(&gpio_lock); - return 0; + goto out; } p = p->next; } gpio_some_alarms = 0; - spin_unlock(&gpio_lock); +out: + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } -/* Main device API. ioctl's to read/set/clear bits, as well as to +/* Main device API. ioctl's to read/set/clear bits, as well as to * set alarms to wait for using a subsequent select(). */ - unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) { - /* Set direction 0=unchanged 1=input, - * return mask with 1=input - */ - unsigned long flags; + /* Set direction 0=unchanged 1=input, + * return mask with 1=input */ if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow &= + *priv->dir = *priv->dir_shadow &= ~((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_in_bits) != arg) && - (arg & changeable_dir_g)) { - arg &= changeable_dir_g; - /* Clear bits in genconfig to set to input */ - if (arg & (1<<0)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_in_bits |= (1<<0); - dir_g_out_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_in_bits |= 0x0000FF00; - dir_g_out_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_in_bits |= 0x00FF0000; - dir_g_out_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_in_bits |= (1<<24); - dir_g_out_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETINPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ - + } + + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_in_bits) != arg) && + (arg & changeable_dir_g)) { + arg &= changeable_dir_g; + /* Clear bits in genconfig to set to input */ + if (arg & (1<<0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_in_bits |= (1<<0); + dir_g_out_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_in_bits; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_in_bits |= 0x0000FF00; + dir_g_out_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_in_bits |= 0x00FF0000; + dir_g_out_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_in_bits |= (1<<24); + dir_g_out_bits &= ~(1<<24); + } + D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ + } - return 0; + return dir_g_in_bits; } /* setget_input */ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) { - unsigned long flags; if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow |= - ((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); + *priv->dir = *priv->dir_shadow |= + ((unsigned char)arg & priv->changeable_dir); return *priv->dir_shadow; - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_out_bits) != arg) && - (arg & changeable_dir_g)) { - /* Set bits in genconfig to set to output */ - if (arg & (1<<0)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_out_bits |= (1<<0); - dir_g_in_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_out_bits |= 0x0000FF00; - dir_g_in_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_out_bits |= 0x00FF0000; - dir_g_in_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_out_bits |= (1<<24); - dir_g_in_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ + } + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_out_bits) != arg) && + (arg & changeable_dir_g)) { + /* Set bits in genconfig to set to output */ + if (arg & (1<<0)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_out_bits |= (1<<0); + dir_g_in_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_out_bits & 0x7FFFFFFF; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_out_bits |= 0x0000FF00; + dir_g_in_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_out_bits |= 0x00FF0000; + dir_g_in_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_out_bits |= (1<<24); + dir_g_in_bits &= ~(1<<24); + } + D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ } - return 0; + return dir_g_out_bits & 0x7FFFFFFF; } /* setget_output */ static int @@ -642,12 +509,11 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; int ret = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + struct gpio_private *priv = file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ @@ -659,7 +525,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } break; case IO_SETBITS: - local_irq_save(flags); // set changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow |= @@ -667,10 +532,8 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_CLRBITS: - local_irq_save(flags); // clear changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow &= @@ -678,7 +541,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_HIGHALARM: // set alarm when bits with 1 in arg go high @@ -698,6 +560,8 @@ gpio_ioctl(struct inode *inode, struct file *file, /* Must update gpio_some_alarms */ struct gpio_private *p = alarmlist; int some_alarms; + spin_lock_irq(&gpio_lock); + p = alarmlist; some_alarms = 0; while (p) { if (p->highalarm | p->lowalarm) { @@ -707,6 +571,7 @@ gpio_ioctl(struct inode *inode, struct file *file, p = p->next; } gpio_some_alarms = some_alarms; + spin_unlock_irq(&gpio_lock); } break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ @@ -766,7 +631,7 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = *R_PORT_G_DATA; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_READ_OUTBITS: @@ -776,33 +641,32 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = port_g_data_shadow; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_input(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) - { + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_output(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; default: @@ -812,7 +676,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ret = -EINVAL; } /* switch */ - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } @@ -824,18 +688,18 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) switch (_IOC_NR(cmd)) { case IO_LEDACTIVE_SET: - green = ((unsigned char) arg) & 1; - red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); + green = ((unsigned char)arg) & 1; + red = (((unsigned char)arg) >> 1) & 1; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; case IO_LED_SETBIT: - LED_BIT_SET(arg); + CRIS_LED_BIT_SET(arg); break; case IO_LED_CLRBIT: - LED_BIT_CLR(arg); + CRIS_LED_BIT_CLR(arg); break; default: @@ -845,7 +709,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +static const struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -854,16 +718,18 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; - -void ioif_watcher(const unsigned int gpio_in_available, - const unsigned int gpio_out_available, - const unsigned char pa_available, - const unsigned char pb_available) +static void ioif_watcher(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available) { unsigned long int flags; - D(printk("gpio.c: ioif_watcher called\n")); - D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", - gpio_in_available, gpio_out_available, pa_available, pb_available)); + + D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); + D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x " + "PA: 0x%02x PB: 0x%02x\n", + gpio_in_available, gpio_out_available, + pa_available, pb_available)); spin_lock_irqsave(&gpio_lock, flags); @@ -872,7 +738,7 @@ void ioif_watcher(const unsigned int gpio_in_available, /* Initialise the dir_g_shadow etc. depending on genconfig */ /* 0=input 1=output */ - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) dir_g_shadow |= (1 << 0); if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) dir_g_shadow |= 0x0000FF00; @@ -884,7 +750,8 @@ void ioif_watcher(const unsigned int gpio_in_available, changeable_dir_g = changeable_dir_g_mask; changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_in_bits; - /* Correct the bits that can change direction */ + + /* Correct the bits that can change direction */ dir_g_out_bits &= ~changeable_dir_g; dir_g_out_bits |= dir_g_shadow; dir_g_in_bits &= ~changeable_dir_g; @@ -892,7 +759,8 @@ void ioif_watcher(const unsigned int gpio_in_available, spin_unlock_irqrestore(&gpio_lock, flags); - printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", + printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX " + "val: %08lX\n", dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", dir_g_shadow, changeable_dir_g); @@ -900,16 +768,12 @@ void ioif_watcher(const unsigned int gpio_in_available, /* main driver initialization routine, called from mem.c */ -static __init int -gpio_init(void) +static int __init gpio_init(void) { int res; #if defined (CONFIG_ETRAX_CSP0_LEDS) int i; #endif - printk("gpio init\n"); - - /* do the formalities */ res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); if (res < 0) { @@ -919,43 +783,45 @@ gpio_init(void) /* Clear all leds */ #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); + CRIS_LED_NETWORK_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); #if defined (CONFIG_ETRAX_CSP0_LEDS) - for (i = 0; i < 32; i++) { - LED_BIT_SET(i); - } + for (i = 0; i < 32; i++) + CRIS_LED_BIT_SET(i); #endif #endif /* The I/O interface allocation watcher will be called when * registering it. */ if (cris_io_interface_register_watcher(ioif_watcher)){ - printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); + printk(KERN_WARNING "gpio_init: Failed to install IO " + "if allocator watcher\n"); } - printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n"); + printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. - */ - if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { + */ + res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name); + if (res) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); + return res; } - if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { + res = request_irq(PA_IRQ_NBR, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name); + if (res) printk(KERN_CRIT "err: PA irq for gpio\n"); - } - return res; } /* this makes sure that gpio_init is called during kernel boot */ - module_init(gpio_init); + diff --git a/arch/cris/arch-v10/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c index aca81ddaf60..d6d22067d0c 100644 --- a/arch/cris/arch-v10/drivers/i2c.c +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -6,85 +6,9 @@ *! kernel modules (i2c_writereg/readreg) and from userspace using *! ioctl()'s *! -*! Nov 30 1998 Torbjorn Eliasson Initial version. -*! Bjorn Wesen Elinux kernel version. -*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - -*! don't use PB_I2C if DS1302 uses same bits, -*! use PB. -*! $Log: i2c.c,v $ -*! Revision 1.13 2005/03/07 13:13:07 starvik -*! Added spinlocks to protect states etc -*! -*! Revision 1.12 2005/01/05 06:11:22 starvik -*! No need to do local_irq_disable after local_irq_save. -*! -*! Revision 1.11 2004/12/13 12:21:52 starvik -*! Added I/O and DMA allocators from Linux 2.4 -*! -*! Revision 1.9 2004/08/24 06:49:14 starvik -*! Whitespace cleanup -*! -*! Revision 1.8 2004/06/08 08:48:26 starvik -*! Removed unused code -*! -*! Revision 1.7 2004/05/28 09:26:59 starvik -*! Modified I2C initialization to work in 2.6. -*! -*! Revision 1.6 2004/05/14 07:58:03 starvik -*! Merge of changes from 2.4 -*! -*! Revision 1.4 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.3 2002/11/20 11:56:11 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.2 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.9 2002/10/31 15:32:26 starvik -*! Update Port B register and shadow even when running with hardware support -*! to avoid glitches when reading bits -*! Never set direction to out in i2c_inbyte -*! Removed incorrect clock toggling at end of i2c_inbyte -*! -*! Revision 1.8 2002/08/13 06:31:53 starvik -*! Made SDA and SCL line configurable -*! Modified i2c_inbyte to work with PCF8563 -*! -*! Revision 1.7 2001/04/04 13:11:36 markusl -*! Updated according to review remarks -*! -*! Revision 1.6 2001/03/19 12:43:00 markusl -*! Made some symbols unstatic (used by the eeprom driver) -*! -*! Revision 1.5 2001/02/27 13:52:48 bjornw -*! malloc.h -> slab.h -*! -*! Revision 1.4 2001/02/15 07:17:40 starvik -*! Corrected usage if port_pb_i2c_shadow -*! -*! Revision 1.3 2001/01/26 17:55:13 bjornw -*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it -*! magically. Config.in needs to set it for the options that need it, like -*! Dallas 1302 support. Actually, it should be default since it screws up -*! the PB bits even if you don't use I2C.. -*! * Include linux/config.h to get the above -*! -*! Revision 1.2 2001/01/18 15:49:30 bjornw -*! 2.4 port of I2C including some cleanups (untested of course) -*! -*! Revision 1.1 2001/01/18 15:35:25 bjornw -*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version -*! -*! -*! --------------------------------------------------------------------------- -*! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -622,7 +546,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * last received byte needs to be nacked * instead of acked */ - i2c_sendack(); + i2c_sendnack(); /* * end sequence */ @@ -708,6 +632,7 @@ i2c_init(void) if (!first) { return res; } + first = 0; /* Setup and enable the Port B I2C interface */ diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c index c263b8232db..52103d16dc6 100644 --- a/arch/cris/arch-v10/drivers/pcf8563.c +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -8,14 +8,13 @@ * low detector are also provided. All address and data are transferred * serially via two-line bidirectional I2C-bus. Maximum bus speed is * 400 kbits/s. The built-in word address register is incremented - * automatically after each written or read bute. + * automatically after each written or read byte. * - * Copyright (c) 2002, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. * - * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ */ #include <linux/module.h> @@ -27,19 +26,19 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> -#include <linux/capability.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> -#include <asm/arch/svinto.h> #include <asm/rtc.h> + #include "i2c.h" -#define PCF8563_MAJOR 121 /* Local major number. */ -#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.11 $" +#define DRIVER_VERSION "$Revision: 1.24 $" /* I2C bus slave registers. */ #define RTC_I2C_READ 0xa3 @@ -49,71 +48,88 @@ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) -static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ - +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; + static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, .ioctl = pcf8563_ioctl, }; unsigned char -pcf8563_readreg(int reg) +pcf8563_readreg(int reg) { - unsigned char res = i2c_readreg(RTC_I2C_READ, reg); - - /* The PCF8563 does not return 0 for unimplemented bits */ - switch(reg) - { - case RTC_SECONDS: - case RTC_MINUTES: - res &= 0x7f; - break; - case RTC_HOURS: - case RTC_DAY_OF_MONTH: - res &= 0x3f; - break; - case RTC_MONTH: - res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ - break; + unsigned char res = rtc_read(reg); + + /* The PCF8563 does not return 0 for unimplemented bits. */ + switch (reg) { + case RTC_SECONDS: + case RTC_MINUTES: + res &= 0x7F; + break; + case RTC_HOURS: + case RTC_DAY_OF_MONTH: + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; + case RTC_MONTH: + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; } return res; } void -pcf8563_writereg(int reg, unsigned char val) +pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } void get_rtc_time(struct rtc_time *tm) { - tm->tm_sec = rtc_read(RTC_SECONDS); - tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); tm->tm_hour = rtc_read(RTC_HOURS); tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); - tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7f; - tm->tm_min &= 0x7f; - tm->tm_hour &= 0x3f; - tm->tm_mday &= 0x3f; - tm->tm_mon &= 0x1f; + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); @@ -126,17 +142,24 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { - int ret; - - if ((ret = i2c_init())) { - printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); - return ret; + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + + /* Initiate the i2c protocol. */ + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; } /* * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq, clear the - * Voltage Low bit, and resetting all alarms. + * clearing control1, control2 and clk freq and resetting + * all alarms. */ if (rtc_write(RTC_CONTROL1, 0x00) < 0) goto err; @@ -147,34 +170,36 @@ pcf8563_init(void) if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) goto err; - /* Clear the VL bit in the seconds register. */ - ret = rtc_read(RTC_SECONDS); - - if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) goto err; - + /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); - - return 0; + + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -187,8 +212,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -198,124 +223,146 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - spin_lock(&rtc_lock); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { - spin_unlock(&rtc_lock); - return -EFAULT; - } - - spin_unlock(&rtc_lock); - return 0; - } - break; - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY - return -EPERM; -#else - int leap; - int century; - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0; - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - spin_lock(&rtc_lock); - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - spin_unlock(&rtc_lock); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - - case RTC_VLOW_RD: - { - int vl_bit = 0; - - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; - - return 0; - } + case RTC_RD_TIME: + { + struct rtc_time tm; - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; + } + + mutex_unlock(&rtc_lock); - return 0; + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) { + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); } - default: - return -ENOTTY; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; + + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ + + int ret = rtc_read(RTC_SECONDS); + + rtc_write(RTC_SECONDS, (ret & 0x7F)); + + /* Clear the cached value. */ + voltage_low = 0; + + return 0; + } + default: + return -ENOTTY; } return 0; } -static int __init -pcf8563_register(void) +static int __init pcf8563_register(void) { - pcf8563_init(); + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", PCF8563_NAME, PCF8563_MAJOR); return -1; } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - return 0; + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } + + return 0; } module_init(pcf8563_register); diff --git a/arch/cris/arch-v10/drivers/sync_serial.c b/arch/cris/arch-v10/drivers/sync_serial.c new file mode 100644 index 00000000000..069546e342c --- /dev/null +++ b/arch/cris/arch-v10/drivers/sync_serial.c @@ -0,0 +1,1441 @@ +/* + * Simple synchronous serial port driver for ETRAX 100LX. + * + * Synchronous serial ports are used for continuous streamed data like audio. + * The default setting for this driver is compatible with the STA 013 MP3 + * decoder. The driver can easily be tuned to fit other audio encoder/decoders + * and SPI + * + * Copyright (c) 2001-2008 Axis Communications AB + * + * Author: Mikael Starvik, Johan Adolfsson + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/arch/svinto.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/sync_serial.h> +#include <asm/arch/io_interface_mux.h> + +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ +/* */ +/* Three DMA descriptors are linked together. Each DMA descriptor is */ +/* responsible for port->bufchunk of a common buffer. */ +/* */ +/* +---------------------------------------------+ */ +/* | +----------+ +----------+ +----------+ | */ +/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ */ +/* +----------+ +----------+ +----------+ */ +/* | | | */ +/* v v v */ +/* +-------------------------------------+ */ +/* | BUFFER | */ +/* +-------------------------------------+ */ +/* |<- data_avail ->| */ +/* readp writep */ +/* */ +/* If the application keeps up the pace readp will be right after writep.*/ +/* If the application can't keep the pace we have to throw away data. */ +/* The idea is that readp should be ready with the data pointed out by */ +/* Descr[i] when the DMA has filled in Descr[i+1]. */ +/* Otherwise we will discard */ +/* the rest of the data pointed out by Descr1 and set readp to the start */ +/* of Descr2 */ + +#define SYNC_SERIAL_MAJOR 125 + +/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ +/* words can be handled */ +#define IN_BUFFER_SIZE 12288 +#define IN_DESCR_SIZE 256 +#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) +#define OUT_BUFFER_SIZE 4096 + +#define DEFAULT_FRAME_RATE 0 +#define DEFAULT_WORD_RATE 7 + +/* NOTE: Enabling some debug will likely cause overrun or underrun, + * especially if manual mode is use. + */ +#define DEBUG(x) +#define DEBUGREAD(x) +#define DEBUGWRITE(x) +#define DEBUGPOLL(x) +#define DEBUGRXINT(x) +#define DEBUGTXINT(x) + +/* Define some macros to access ETRAX 100 registers */ +#define SETF(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_FIELD_(reg##_, field##_, val); \ + } while (0) + +#define SETS(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val); \ + } while (0) + +struct sync_port { + /* Etrax registers and bits*/ + const volatile unsigned *const status; + volatile unsigned *const ctrl_data; + volatile unsigned *const output_dma_first; + volatile unsigned char *const output_dma_cmd; + volatile unsigned char *const output_dma_clr_irq; + volatile unsigned *const input_dma_first; + volatile unsigned char *const input_dma_cmd; + volatile unsigned *const input_dma_descr; + /* 8*4 */ + volatile unsigned char *const input_dma_clr_irq; + volatile unsigned *const data_out; + const volatile unsigned *const data_in; + char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */ + + char output_dma_bit; /* In R_IRQ_MASK2_RD */ + /* End of fields initialised in array */ + char started; /* 1 if port has been started */ + char port_nbr; /* Port 0 or 1 */ + char busy; /* 1 if port is busy */ + + char enabled; /* 1 if port is enabled */ + char use_dma; /* 1 if port uses dma */ + char tr_running; + + char init_irqs; + + /* Register shadow */ + unsigned int ctrl_data_shadow; + /* Remaining bytes for current transfer */ + volatile unsigned int out_count; + /* Current position in out_buffer */ + unsigned char *outp; + /* 16*4 */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + + unsigned int in_buffer_size; + unsigned int inbufchunk; + struct etrax_dma_descr out_descr __attribute__ ((aligned(32))); + struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32))); + unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); + unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); + unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); + struct etrax_dma_descr *next_rx_desc; + struct etrax_dma_descr *prev_rx_desc; + int full; + + wait_queue_head_t out_wait_q; + wait_queue_head_t in_wait_q; +}; + + +static int etrax_sync_serial_init(void); +static void initialize_port(int portnbr); +static inline int sync_data_avail(struct sync_port *port); + +static int sync_serial_open(struct inode *inode, struct file *file); +static int sync_serial_release(struct inode *inode, struct file *file); +static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos); +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_DMA +#endif + +static void send_word(struct sync_port *port); +static void start_dma(struct sync_port *port, const char *data, int count); +static void start_dma_in(struct sync_port *port); +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); +#endif +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_MANUAL +#endif +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +/* The ports */ +static struct sync_port ports[] = { + { + .status = R_SYNC_SERIAL1_STATUS, + .ctrl_data = R_SYNC_SERIAL1_CTRL, + .output_dma_first = R_DMA_CH8_FIRST, + .output_dma_cmd = R_DMA_CH8_CMD, + .output_dma_clr_irq = R_DMA_CH8_CLR_INTR, + .input_dma_first = R_DMA_CH9_FIRST, + .input_dma_cmd = R_DMA_CH9_CMD, + .input_dma_descr = R_DMA_CH9_DESCR, + .input_dma_clr_irq = R_DMA_CH9_CLR_INTR, + .data_out = R_SYNC_SERIAL1_TR_DATA, + .data_in = R_SYNC_SERIAL1_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + }, + { + .status = R_SYNC_SERIAL3_STATUS, + .ctrl_data = R_SYNC_SERIAL3_CTRL, + .output_dma_first = R_DMA_CH4_FIRST, + .output_dma_cmd = R_DMA_CH4_CMD, + .output_dma_clr_irq = R_DMA_CH4_CLR_INTR, + .input_dma_first = R_DMA_CH5_FIRST, + .input_dma_cmd = R_DMA_CH5_CMD, + .input_dma_descr = R_DMA_CH5_DESCR, + .input_dma_clr_irq = R_DMA_CH5_CLR_INTR, + .data_out = R_SYNC_SERIAL3_TR_DATA, + .data_in = R_SYNC_SERIAL3_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + } +}; + +/* Register shadows */ +static unsigned sync_serial_prescale_shadow; + +#define NUMBER_OF_PORTS 2 + +static struct file_operations sync_serial_fops = { + .owner = THIS_MODULE, + .write = sync_serial_write, + .read = sync_serial_read, + .poll = sync_serial_poll, + .ioctl = sync_serial_ioctl, + .open = sync_serial_open, + .release = sync_serial_release +}; + +static int __init etrax_sync_serial_init(void) +{ + ports[0].enabled = 0; + ports[1].enabled = 0; + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) { + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 0); + return -EBUSY; + } +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 1); + return -EBUSY; + } +#endif + + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + cris_free_io_interface(if_sync_serial_3); +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk("unable to get major for synchronous serial port\n"); + return -EBUSY; + } + + /* Deselect synchronous serial ports while configuring. */ + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + /* Initialize Ports */ +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + ports[0].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + ports[0].use_dma = 1; +#else + ports[0].use_dma = 0; +#endif + initialize_port(0); +#endif + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + ports[1].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + ports[1].use_dma = 1; +#else + ports[1].use_dma = 0; +#endif + initialize_port(1); +#endif + + *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */ + + /* Set up timing */ + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = ( + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, + DEFAULT_FRAME_RATE) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal)); + + /* Select synchronous ports */ + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + printk(KERN_INFO "ETRAX 100LX synchronous serial port driver\n"); + return 0; +} + +static void __init initialize_port(int portnbr) +{ + struct sync_port *port = &ports[portnbr]; + + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); + + port->started = 0; + port->port_nbr = portnbr; + port->busy = 0; + port->tr_running = 0; + + port->out_count = 0; + port->outp = port->out_buffer; + + port->readp = port->flip; + port->writep = port->flip; + port->in_buffer_size = IN_BUFFER_SIZE; + port->inbufchunk = IN_DESCR_SIZE; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc->ctrl = d_eol; + + init_waitqueue_head(&port->out_wait_q); + init_waitqueue_head(&port->in_wait_q); + + port->ctrl_data_shadow = + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) | + IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | + IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) | + IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) | + IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) | + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)| + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high); + + if (port->use_dma) + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, on); + else + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, off); + + *port->ctrl_data = port->ctrl_data_shadow; +} + +static inline int sync_data_avail(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- - ----- + * ^rp ^wp ^wp ^rp + */ + if (end >= start) + avail = end - start; + else + avail = port->in_buffer_size - (start - end); + return avail; +} + +static inline int sync_data_avail_to_end(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + return avail; +} + + +static int sync_serial_open(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + int mode; + + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + /* Allow open this device twice (assuming one reader and one writer) */ + if (port->busy == 2) { + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); + return -EBUSY; + } + if (port->init_irqs) { + if (port->use_dma) { + if (port == &ports[0]) { +#ifdef SYNC_SER_DMA + if (request_irq(24, tr_interrupt, 0, + "synchronous serial 1 dma tr", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (request_irq(25, rx_interrupt, 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(24, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (cris_request_dma(8, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(9, + "synchronous serial 1 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + cris_free_dma(8, NULL); + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(8); WAIT_DMA(8); + RESET_DMA(9); WAIT_DMA(9); + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, + do); + *R_DMA_CH9_CLR_INTR = + IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma8_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma9_descr, + set); + } else if (port == &ports[1]) { +#ifdef SYNC_SER_DMA + if (request_irq(20, tr_interrupt, 0, + "synchronous serial 3 dma tr", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (request_irq(21, rx_interrupt, 0, + "synchronous serial 3 dma rx", + &ports[1])) { + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (cris_request_dma(4, + "synchronous serial 3 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(5, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + cris_free_dma(4, NULL); + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(4); WAIT_DMA(4); + RESET_DMA(5); WAIT_DMA(5); + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, + do); + *R_DMA_CH5_CLR_INTR = + IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma4_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma5_descr, + set); + } + start_dma_in(port); + port->init_irqs = 0; + } else { /* !port->use_dma */ +#ifdef SYNC_SER_MANUAL + if (port == &ports[0]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } else if (port == &ports[1]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } + port->init_irqs = 0; +#else + panic("sync_serial: Manual mode not supported.\n"); +#endif /* SYNC_SER_MANUAL */ + } + } /* port->init_irqs */ + + port->busy++; + /* Start port if we use it as input */ + mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow); + if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + *port->ctrl_data = port->ctrl_data_shadow; + if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev)); + } + return 0; +} + +static int sync_serial_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + if (port->busy) + port->busy--; + if (!port->busy) + *R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) | + (1 << port->transmitter_ready_bit)); + + return 0; +} + + + +static unsigned int sync_serial_poll(struct file *file, poll_table *wait) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned int mask = 0; + struct sync_port *port; + DEBUGPOLL(static unsigned int prev_mask = 0); + + port = &ports[dev]; + poll_wait(file, &port->out_wait_q, wait); + poll_wait(file, &port->in_wait_q, wait); + /* Some room to write */ + if (port->out_count < OUT_BUFFER_SIZE) + mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ + if (sync_data_avail(port) >= port->inbufchunk) + mask |= POLLIN | POLLRDNORM; + + DEBUGPOLL(if (mask != prev_mask) + printk(KERN_DEBUG "sync_serial_poll: mask 0x%08X %s %s\n", + mask, + mask & POLLOUT ? "POLLOUT" : "", + mask & POLLIN ? "POLLIN" : ""); + prev_mask = mask; + ); + return mask; +} + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int return_val = 0; + unsigned long flags; + + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -1; + } + port = &ports[dev]; + + local_irq_save(flags); + /* Disable port while changing config */ + if (dev) { + if (port->use_dma) { + RESET_DMA(4); WAIT_DMA(4); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + } else { + if (port->use_dma) { + RESET_DMA(8); WAIT_DMA(8); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + } + *R_GEN_CONFIG_II = gen_config_ii_shadow; + local_irq_restore(flags); + + switch (cmd) { + case SSP_SPEED: + if (GET_SPEED(arg) == CODEC) { + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + codec); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + codec); + + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, prescaler, + GET_FREQ(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, frame_rate, + GET_FRAME_RATE(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, word_rate, + GET_WORD_RATE(arg)); + } else { + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + tr_baud, GET_SPEED(arg)); + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + baudrate); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + baudrate); + } + break; + case SSP_MODE: + if (arg > 5) + return -EINVAL; + if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT) + *R_IRQ_MASK1_CLR = 1 << port->data_avail_bit; + else if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg); + break; + case SSP_FRAME_SYNC: + if (arg & NORMAL_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, normal); + else if (arg & EARLY_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, early); + + if (arg & BIT_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, bit); + else if (arg & WORD_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, word); + else if (arg & EXTENDED_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, extended); + + if (arg & SYNC_ON) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, on); + else if (arg & SYNC_OFF) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, off); + + if (arg & WORD_SIZE_8) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size8bit); + else if (arg & WORD_SIZE_12) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size12bit); + else if (arg & WORD_SIZE_16) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size16bit); + else if (arg & WORD_SIZE_24) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size24bit); + else if (arg & WORD_SIZE_32) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size32bit); + + if (arg & BIT_ORDER_MSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, msb); + else if (arg & BIT_ORDER_LSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, lsb); + + if (arg & FLOW_CONTROL_ENABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, enabled); + else if (arg & FLOW_CONTROL_DISABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, disabled); + + if (arg & CLOCK_NOT_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, normal); + else if (arg & CLOCK_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, gated); + + break; + case SSP_IPOLARITY: + /* NOTE!! negedge is considered NORMAL */ + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, pos); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, inverted); + break; + case SSP_OPOLARITY: + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, normal); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, inverted); + break; + case SSP_SPI: + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, + disabled); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, + msb); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, + size8bit); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, + word); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, + normal); + if (arg & SPI_SLAVE) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, SLAVE_INPUT); + } else { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, MASTER_OUTPUT); + } + break; + case SSP_INBUFCHUNK: +#if 0 + if (arg > port->in_buffer_size/NUM_IN_DESCR) + return -EINVAL; + port->inbufchunk = arg; + /* Make sure in_buffer_size is a multiple of inbufchunk */ + port->in_buffer_size = + (port->in_buffer_size/port->inbufchunk) * + port->inbufchunk; + DEBUG(printk(KERN_DEBUG "inbufchunk %i in_buffer_size: %i\n", + port->inbufchunk, port->in_buffer_size)); + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } +#endif + break; + default: + return_val = -1; + } + /* Make sure we write the config without interruption */ + local_irq_save(flags); + /* Set config and enable port */ + *port->ctrl_data = port->ctrl_data_shadow; + nop(); nop(); nop(); nop(); + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow; + nop(); nop(); nop(); nop(); + if (dev) + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); + else + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); + + *R_GEN_CONFIG_II = gen_config_ii_shadow; + /* Reset DMA. At readout from serial port the data could be shifted + * one byte if not resetting DMA. + */ + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } + local_irq_restore(flags); + return return_val; +} + + +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct sync_port *port; + unsigned long flags; + unsigned long c, c1; + unsigned long free_outp; + unsigned long outp; + unsigned long out_buffer; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu (%d/%d)\n", + port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); + /* Space to end of buffer */ + /* + * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE + * outp^ +out_count + * ^free_outp + * out_buffer 45<- c ->0123OUT_BUFFER_SIZE + * +out_count outp^ + * free_outp + * + */ + + /* Read variables that may be updated by interrupts */ + local_irq_save(flags); + if (count > OUT_BUFFER_SIZE - port->out_count) + count = OUT_BUFFER_SIZE - port->out_count; + + outp = (unsigned long)port->outp; + free_outp = outp + port->out_count; + local_irq_restore(flags); + out_buffer = (unsigned long)port->out_buffer; + + /* Find out where and how much to write */ + if (free_outp >= out_buffer + OUT_BUFFER_SIZE) + free_outp -= OUT_BUFFER_SIZE; + if (free_outp >= outp) + c = out_buffer + OUT_BUFFER_SIZE - free_outp; + else + c = outp - free_outp; + if (c > count) + c = count; + + DEBUGWRITE(printk(KERN_DEBUG "w op %08lX fop %08lX c %lu\n", + outp, free_outp, c)); + if (copy_from_user((void *)free_outp, buf, c)) + return -EFAULT; + + if (c != count) { + buf += c; + c1 = count - c; + DEBUGWRITE(printk(KERN_DEBUG "w2 fi %lu c %lu c1 %lu\n", + free_outp-out_buffer, c, c1)); + if (copy_from_user((void *)out_buffer, buf, c1)) + return -EFAULT; + } + local_irq_save(flags); + port->out_count += count; + local_irq_restore(flags); + + /* Make sure transmitter/receiver is running */ + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + + *port->ctrl_data = port->ctrl_data_shadow; + + if (file->f_flags & O_NONBLOCK) { + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << + port->transmitter_ready_bit; + } else + start_dma(port, + (unsigned char *volatile)port->outp, c); + } + local_irq_restore(flags); + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu NB\n", + port->port_nbr, count)); + return count; + } + + /* Sleep until all sent */ + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit; + } else + start_dma(port, port->outp, c); + } + local_irq_restore(flags); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) + return -EINTR; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", port->port_nbr, count)); + return count; +} + +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + int avail; + struct sync_port *port; + unsigned char *start; + unsigned char *end; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGREAD(printk(KERN_DEBUG "R%d c %d ri %lu wi %lu /%lu\n", + dev, count, port->readp - port->flip, + port->writep - port->flip, port->in_buffer_size)); + + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + *port->ctrl_data = port->ctrl_data_shadow; + + /* Calculate number of available bytes */ + /* Save pointers to avoid that they are modified by interrupt */ + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + while (start == end && !port->full) { + /* No data */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&port->in_wait_q); + if (signal_pending(current)) + return -EINTR; + + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + } + + /* Lazy read, never return wrapped data. */ + if (port->full) + avail = port->in_buffer_size; + else if (end > start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + + count = count > avail ? avail : count; + if (copy_to_user(buf, start, count)) + return -EFAULT; + /* Disable interrupts while updating readp */ + local_irq_save(flags); + port->readp += count; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + port->full = 0; + local_irq_restore(flags); + DEBUGREAD(printk(KERN_DEBUG "r %d\n", count)); + return count; +} + +static void send_word(struct sync_port *port) +{ + switch (IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize, + port->ctrl_data_shadow)) { + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + port->out_count--; + *port->data_out = *port->outp++; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = (*port->outp++) << 8; + data |= *port->outp++; + port->out_count -= 2; + *port->data_out = data; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + port->out_count -= 2; + *port->data_out = *(unsigned short *)port->outp; + port->outp += 2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + port->out_count -= 3; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 3; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + port->out_count -= 4; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 4; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } +} + + +static void start_dma(struct sync_port *port, const char *data, int count) +{ + port->tr_running = 1; + port->out_descr.hw_len = 0; + port->out_descr.next = 0; + port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */ + port->out_descr.sw_len = count; + port->out_descr.buf = virt_to_phys(data); + port->out_descr.status = 0; + + *port->output_dma_first = virt_to_phys(&port->out_descr); + *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); + DEBUGTXINT(printk(KERN_DEBUG "dma %08lX c %d\n", + (unsigned long)data, count)); +} + +static void start_dma_in(struct sync_port *port) +{ + int i; + unsigned long buf; + port->writep = port->flip; + + if (port->writep > port->flip + port->in_buffer_size) { + panic("Offset too large in sync serial driver\n"); + return; + } + buf = virt_to_phys(port->in_buffer); + for (i = 0; i < NUM_IN_DESCR; i++) { + port->in_descr[i].sw_len = port->inbufchunk; + port->in_descr[i].ctrl = d_int; + port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]); + port->in_descr[i].buf = buf; + port->in_descr[i].hw_len = 0; + port->in_descr[i].status = 0; + port->in_descr[i].fifo_len = 0; + buf += port->inbufchunk; + prepare_rx_descriptor(&port->in_descr[i]); + } + /* Link the last descriptor to the first */ + port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]); + port->in_descr[i-1].ctrl |= d_eol; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + *port->input_dma_first = virt_to_phys(port->next_rx_desc); + *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); +} + +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + struct etrax_dma_descr *descr; + unsigned int sentl; + int handled = 0; + int i; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + if (!port->enabled || !port->use_dma) + continue; + + /* IRQ active for the port? */ + if (!(ireg & (1 << port->output_dma_bit))) + continue; + + handled = 1; + + /* Clear IRQ */ + *port->output_dma_clr_irq = + IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); + + descr = &port->out_descr; + if (!(descr->status & d_stop)) + sentl = descr->sw_len; + else + /* Otherwise find amount of data sent here */ + sentl = descr->hw_len; + + port->out_count -= sentl; + port->outp += sentl; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + if (port->out_count) { + int c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; + if (c > port->out_count) + c = port->out_count; + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMAWRITE %i %i\n", sentl, c)); + start_dma(port, port->outp, c); + } else { + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMA stop %i\n", sentl)); + port->tr_running = 0; + } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); + } + return IRQ_RETVAL(handled); +} /* tr_interrupt */ + +static irqreturn_t rx_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || !port->use_dma) + continue; + + if (!(ireg & (1 << port->input_dma_descr_bit))) + continue; + + /* Descriptor interrupt */ + handled = 1; + while (*port->input_dma_descr != + virt_to_phys(port->next_rx_desc)) { + if (port->writep + port->inbufchunk > port->flip + + port->in_buffer_size) { + int first_size = port->flip + + port->in_buffer_size - port->writep; + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + first_size); + memcpy(port->flip, + phys_to_virt(port->next_rx_desc->buf + + first_size), + port->inbufchunk - first_size); + port->writep = port->flip + + port->inbufchunk - first_size; + } else { + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + port->inbufchunk); + port->writep += port->inbufchunk; + if (port->writep >= port->flip + + port->in_buffer_size) + port->writep = port->flip; + } + if (port->writep == port->readp) + port->full = 1; + prepare_rx_descriptor(port->next_rx_desc); + port->next_rx_desc->ctrl |= d_eol; + port->prev_rx_desc->ctrl &= ~d_eol; + port->prev_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc); + port->next_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc->next); + /* Wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); + *port->input_dma_cmd = IO_STATE(R_DMA_CH1_CMD, + cmd, restart); + /* DMA has reached end of descriptor */ + *port->input_dma_clr_irq = IO_STATE(R_DMA_CH0_CLR_INTR, + clr_descr, do); + } + } + return IRQ_RETVAL(handled); +} /* rx_interrupt */ +#endif /* SYNC_SER_DMA */ + +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id) +{ + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || port->use_dma) + continue; + + /* Data received? */ + if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) { + handled = 1; + /* Read data */ + switch (port->ctrl_data_shadow & + IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) { + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + *port->writep++ = + *(volatile char *)port->data_in; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = *(unsigned short *)port->data_in; + *port->writep = (data & 0x0ff0) >> 4; + *(port->writep + 1) = data & 0x0f; + port->writep += 2; + break; + } + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + *(unsigned short *)port->writep = + *(volatile unsigned short *)port->data_in; + port->writep += 2; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 3; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 4; + break; + } + + /* Wrap? */ + if (port->writep >= port->flip + port->in_buffer_size) + port->writep = port->flip; + if (port->writep == port->readp) { + /* Receive buffer overrun, discard oldest */ + port->readp++; + /* Wrap? */ + if (port->readp >= port->flip + + port->in_buffer_size) + port->readp = port->flip; + } + if (sync_data_avail(port) >= port->inbufchunk) { + /* Wake up application */ + wake_up_interruptible(&port->in_wait_q); + } + } + + /* Transmitter ready? */ + if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) { + if (port->out_count > 0) { + /* More data to send */ + send_word(port); + } else { + /* Transmission finished */ + /* Turn off IRQ */ + *R_IRQ_MASK1_CLR = 1 << + port->transmitter_ready_bit; + /* Wake up application */ + wake_up_interruptible(&port->out_wait_q); + } + } + } + return IRQ_RETVAL(handled); +} +#endif + +module_init(etrax_sync_serial_init); diff --git a/arch/cris/arch-v10/kernel/debugport.c b/arch/cris/arch-v10/kernel/debugport.c index 93679a48c79..04d5eee2c90 100644 --- a/arch/cris/arch-v10/kernel/debugport.c +++ b/arch/cris/arch-v10/kernel/debugport.c @@ -1,6 +1,6 @@ /* Serialport functions for debugging * - * Copyright (c) 2000 Axis Communications AB + * Copyright (c) 2000-2007 Axis Communications AB * * Authors: Bjorn Wesen * @@ -11,96 +11,6 @@ * enableDebugIRQ() * init_etrax_debug() * - * $Log: debugport.c,v $ - * Revision 1.27 2005/06/10 10:34:14 starvik - * Real console support - * - * Revision 1.26 2005/06/07 07:06:07 starvik - * Added LF->CR translation to make ETRAX customers happy. - * - * Revision 1.25 2005/03/08 08:56:47 mikaelam - * Do only set index as port->index if port is defined, otherwise use the index from the command line - * - * Revision 1.24 2005/01/19 10:26:33 mikaelam - * Return the cris serial driver in console device driver callback function - * - * Revision 1.23 2005/01/14 10:12:17 starvik - * KGDB on separate port. - * Console fixes from 2.4. - * - * Revision 1.22 2005/01/11 16:06:13 starvik - * typo - * - * Revision 1.21 2005/01/11 13:49:14 starvik - * Added raw_printk to be used where we don't trust the console. - * - * Revision 1.20 2004/12/27 11:18:32 starvik - * Merge of Linux 2.6.10 (not functional yet). - * - * Revision 1.19 2004/10/21 07:26:16 starvik - * Made it possible to specify console settings on kernel command line. - * - * Revision 1.18 2004/10/19 13:07:37 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.17 2004/09/29 10:33:46 starvik - * Resolved a dealock when printing debug from kernel. - * - * Revision 1.16 2004/08/24 06:12:19 starvik - * Whitespace cleanup - * - * Revision 1.15 2004/08/16 12:37:19 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.14 2004/05/17 13:11:29 starvik - * Disable DMA until real serial driver is up - * - * Revision 1.13 2004/05/14 07:58:01 starvik - * Merge of changes from 2.4 - * - * Revision 1.12 2003/09/11 07:29:49 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.11 2003/07/07 09:53:36 starvik - * Revert all the 2.5.74 merge changes to make the console work again - * - * Revision 1.9 2003/02/17 17:07:23 starvik - * Solved the problem with corrupted debug output (from Linux 2.4) - * * Wait until DMA, FIFO and pipe is empty before and after transmissions - * * Buffer data until a FIFO flush can be triggered. - * - * Revision 1.8 2003/01/22 06:48:36 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.7 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.6 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.5 2002/11/20 06:58:03 starvik - * Compiles with kgdb - * - * Revision 1.4 2002/11/19 14:35:24 starvik - * Changes from linux 2.4 - * Changed struct initializer syntax to the currently preferred notation - * - * Revision 1.3 2002/11/06 09:47:03 starvik - * Modified for new interrupt macros - * - * Revision 1.2 2002/01/21 15:21:50 bjornw - * Update for kdev_t changes - * - * Revision 1.6 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.5 2001/03/26 14:22:05 bjornw - * Namechange of some config options - * - * Revision 1.4 2000/10/06 12:37:26 bjornw - * Use physical addresses when talking to DMA - * - * */ #include <linux/console.h> @@ -112,6 +22,8 @@ #include <asm/arch/svinto.h> #include <asm/io.h> /* Get SIMCOUT. */ +extern void reset_watchdog(void); + struct dbg_port { unsigned int index; @@ -188,7 +100,9 @@ struct dbg_port ports[]= } }; +#ifdef CONFIG_ETRAX_SERIAL extern struct tty_driver *serial_driver; +#endif struct dbg_port* port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) @@ -368,11 +282,12 @@ console_write_direct(struct console *co, const char *buf, unsigned int len) { int i; unsigned long flags; - local_irq_save(flags); if (!port) return; + local_irq_save(flags); + /* Send data */ for (i = 0; i < len; i++) { /* LF -> CRLF */ @@ -386,26 +301,16 @@ console_write_direct(struct console *co, const char *buf, unsigned int len) ; *port->write = buf[i]; } - local_irq_restore(flags); -} -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - static int first = 1; - if (first) { - /* Force reinitialization of the port to get manual mode. */ - port->started = 0; - start_port(port); - first = 0; - } - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write_direct(NULL, buf, strlen(buf)); - return printed_len; + /* + * Feed the watchdog, otherwise it will reset the chip during boot. + * The time to send an ordinary boot message line (10-90 chars) + * varies between 1-8ms at 115200. What makes up for the additional + * 90ms that allows the watchdog to bite? + */ + reset_watchdog(); + + local_irq_restore(flags); } static void @@ -500,6 +405,7 @@ console_setup(struct console *co, char *options) return 0; } + /* This is a dummy serial device that throws away anything written to it. * This is used when no debug output is wanted. */ @@ -555,7 +461,13 @@ etrax_console_device(struct console* co, int *index) { if (port) *index = port->index; + else + *index = 0; +#ifdef CONFIG_ETRAX_SERIAL return port ? serial_driver : &dummy_driver; +#else + return &dummy_driver; +#endif } static struct console sercons = { diff --git a/arch/cris/arch-v10/kernel/dma.c b/arch/cris/arch-v10/kernel/dma.c index e9a0311b141..eb1fa0d2b49 100644 --- a/arch/cris/arch-v10/kernel/dma.c +++ b/arch/cris/arch-v10/kernel/dma.c @@ -1,6 +1,5 @@ /* Wrapper for DMA channel allocator that updates DMA client muxing. - * Copyright 2004, Axis Communications AB - * $Id: dma.c,v 1.1 2004/12/13 12:21:51 starvik Exp $ + * Copyright 2004-2007, Axis Communications AB */ #include <linux/kernel.h> diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S index d1361dc119e..3a65f322ae0 100644 --- a/arch/cris/arch-v10/kernel/entry.S +++ b/arch/cris/arch-v10/kernel/entry.S @@ -1,252 +1,9 @@ -/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $ - * +/* * linux/arch/cris/entry.S * * Copyright (C) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) - * - * $Log: entry.S,v $ - * Revision 1.28 2005/06/20 05:06:30 starvik - * Remove unnecessary diff to kernel.org tree - * - * Revision 1.27 2005/03/04 08:16:16 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.26 2005/01/11 13:49:47 starvik - * Added NMI handler. - * - * Revision 1.25 2004/12/27 11:18:32 starvik - * Merge of Linux 2.6.10 (not functional yet). - * - * Revision 1.24 2004/12/22 10:41:23 starvik - * Updates to make v10 compile with the latest SMP aware generic code (even - * though v10 will never have SMP). - * - * Revision 1.23 2004/10/19 13:07:37 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.22 2004/06/21 10:29:55 starvik - * Merge of Linux 2.6.7 - * - * Revision 1.21 2004/06/09 05:30:27 starvik - * Clean up multiple interrupt handling. - * Prevent interrupts from interrupting each other. - * Handle all active interrupts. - * - * Revision 1.20 2004/06/08 08:55:32 starvik - * Removed unused code - * - * Revision 1.19 2004/06/04 11:56:15 starvik - * Implemented page table lookup for refills in assembler for improved performance. - * - * Revision 1.18 2004/05/11 12:28:25 starvik - * Merge of Linux 2.6.6 - * - * Revision 1.17 2003/09/11 07:29:49 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.16 2003/07/04 08:27:41 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.15 2003/04/09 07:32:55 starvik - * resume should return task_struct, not thread_info - * - * Revision 1.14 2003/04/09 05:20:44 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.13 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c - * - * Revision 1.12 2002/12/10 09:00:10 starvik - * Merge of Linux 2.5.51 - * - * Revision 1.11 2002/12/05 07:53:10 starvik - * Corrected constants used with btstq - * - * Revision 1.10 2002/11/27 08:45:10 starvik - * pid is in task_struct, not thread_info - * - * Revision 1.9 2002/11/26 09:52:05 starvik - * Added preemptive kernel scheduling (if CONFIG_PREEMPT) - * - * Revision 1.8 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.7 2002/11/18 13:02:42 starvik - * Added fourth parameter to do_notify_resume - * Minor cleanup - * - * Revision 1.6 2002/11/11 10:37:50 starvik - * Use new asm-offset defines - * Modified for new location of current->work etc - * Removed SYMBOL_NAME from syscalls - * Added some new syscalls - * - * Revision 1.5 2002/11/05 06:45:11 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.4 2002/02/05 15:41:31 bjornw - * Rewritten to conform better to current 2.5 code (similar to arch/i386) - * - * Revision 1.3 2002/01/21 15:22:20 bjornw - * NICE_DOGGY fix from 2.4 arch/cris - * - * Revision 1.37 2001/12/07 17:03:55 bjornw - * Call a c-hook called watchdog_bite_hook instead of show_registers directly - * - * Revision 1.36 2001/11/22 13:36:36 bjornw - * * In ret_from_intr, check regs->dccr for usermode reentrance instead of - * DCCR explicitly (because the latter might not reflect current reality) - * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before - * since $r9 is call-clobbered and is potentially needed afterwards - * - * Revision 1.35 2001/10/30 17:10:15 bjornw - * Add some syscalls - * - * Revision 1.34 2001/10/01 14:45:03 bjornw - * Removed underscores and added register prefixes - * - * Revision 1.33 2001/08/21 13:48:01 jonashg - * Added fix by HP to avoid oops when doing a hard_reset_now. - * - * Revision 1.32 2001/08/14 04:32:02 hp - * In _resume, add comment why R9 is saved; don't sound like it's call-saved. - * - * Revision 1.31 2001/07/25 16:07:42 bjornw - * softirq_active/mask -> softirq_pending only - * - * Revision 1.30 2001/07/05 01:03:32 hp - * - include asm/errno.h to get ENOSYS. - * - Use ENOSYS, not local constant LENOSYS; tweak comments. - * - Explain why .include, not #include is used. - * - Make oops-register-dump if watchdog bits and it's not expected. - * - Don't jsr, use jump _hard_reset_now, and skip spurious nop. - * - Use correct section attribute for section .rodata. - * - Adjust sys_ni_syscall fill number. - * - * Revision 1.29 2001/06/25 14:07:00 hp - * Fix review comment. - * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of - * magic numbers. Add comment that -traditional must not be used. - * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. - * Correct and update comment. - * * Makefile (.S.o): Don't use -traditional. Add comment why the - * toplevel rule can't be used (now that there's a reason). - * - * Revision 1.28 2001/06/21 02:00:40 hp - * * entry.S: Include asm/unistd.h. - * (_sys_call_table): Use section .rodata, not .data. - * (_kernel_thread): Move from... - * * process.c: ... here. - * * entryoffsets.c (VAL): Break out from... - * (OF): Use VAL. - * (LCLONE_VM): New asmified value from CLONE_VM. - * - * Revision 1.27 2001/05/29 11:25:27 markusl - * In case of "spurious_interrupt", do hard_reset instead of hanging system in a loop... - * - * Revision 1.26 2001/05/15 15:46:03 bjornw - * Include config.h now that we use some CONFIG_ options - * - * Revision 1.25 2001/05/15 05:38:47 hp - * Tweaked code in _ret_from_sys_call - * - * Revision 1.24 2001/05/15 05:27:49 hp - * Save r9 in r1 over function call rather than on stack. - * - * Revision 1.23 2001/05/15 05:10:00 hp - * Generate entry.S structure offsets from C - * - * Revision 1.22 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.21 2001/04/17 11:33:29 orjanf - * Updated according to review: - * * Included asm/sv_addr_ag.h to get macro for internal register. - * * Corrected comment regarding system call argument passing. - * * Removed comment about instruction being in a delay slot. - * * Added comment about SYMBOL_NAME macro. - * - * Revision 1.20 2001/04/12 08:51:07 hp - * - Add entry for sys_fcntl64. In fact copy last piece from i386 including ... - * - .rept to fill table to safe state with sys_ni_syscall. - * - * Revision 1.19 2001/04/04 09:43:32 orjanf - * * Moved do_sigtrap from traps.c to entry.S. - * * LTASK_PID need not be global anymore. - * - * Revision 1.18 2001/03/26 09:25:02 markusl - * Updated after review, should now handle USB interrupts correctly. - * - * Revision 1.17 2001/03/21 16:12:55 bjornw - * * Always make room for the cpu status record in the frame, in order to - * use the same framelength and layout for both mmu busfaults and normal - * irqs. No need to check for the explicit CRIS_FRAME_FIXUP type anymore. - * * Fixed bug with using addq for popping the stack in the epilogue - it - * destroyed the flag register. Use instructions that don't affect the - * flag register instead. - * * Removed write to R_PORT_PA_DATA during spurious_interrupt - * - * Revision 1.16 2001/03/20 19:43:02 bjornw - * * Get rid of esp0 setting - * * Give a 7th argument to a systemcall - the stackframe - * - * Revision 1.15 2001/03/05 13:14:30 bjornw - * Spelling fix - * - * Revision 1.14 2001/02/23 08:36:36 perf - * New ABI; syscallnr=r9, arg5=mof, arg6=srp. - * Corrected tracesys call check. - * - * Revision 1.13 2001/02/15 08:40:55 perf - * H-P by way of perf; - * - (_system_call): Don't read system call function address into r1. - * - (RBFExit): There is no such thing as a null pop. Adjust sp by addq. - * - (_system_call): Don't use r10 and don't save and restore it. - * - (THREAD_ESP0): New constant. - * - (_system_call): Inline set_esp0. - * - * Revision 1.12 2001/01/31 17:56:25 orjanf - * Added definition of LTASK_PID and made it global. - * - * Revision 1.11 2001/01/10 21:13:29 bjornw - * SYMBOL_NAME is defined incorrectly for the compiler options we currently use - * - * Revision 1.10 2000/12/18 23:47:56 bjornw - * * Added syscall trace support (ptrace), completely untested of course - * * Removed redundant check for NULL entries in syscall_table - * - * Revision 1.9 2000/11/21 16:40:51 bjornw - * * New frame type used when an SBFS frame needs to be popped without - * actually restarting the instruction - * * Enable interrupts in signal_return (they did so in x86, I hope it's a good - * idea) - * - * Revision 1.8 2000/11/17 16:53:35 bjornw - * Added detection of frame-type in Rexit, so that mmu_bus_fault can - * use ret_from_intr in the return-path to check for signals (like SEGV) - * and other foul things that might have occurred during the fault. - * - * Revision 1.7 2000/10/06 15:04:28 bjornw - * Include mof in register savings - * - * Revision 1.6 2000/09/12 16:02:44 bjornw - * Linux-2.4.0-test7 derived updates - * - * Revision 1.5 2000/08/17 15:35:15 bjornw - * 2.4.0-test6 changed local_irq_count and friends API - * - * Revision 1.4 2000/08/02 13:59:30 bjornw - * Removed olduname and uname from the syscall list - * - * Revision 1.3 2000/07/31 13:32:58 bjornw - * * Export ret_from_intr - * * _resume updated (prev/last tjohejsan) - * * timer_interrupt obsolete - * * SIGSEGV detection in mmu_bus_fault temporarily disabled - * - * */ /* @@ -1167,9 +924,11 @@ sys_call_table: .long sys_epoll_pwait .long sys_utimensat /* 320 */ .long sys_signalfd - .long sys_ni_syscall + .long sys_timerfd_create .long sys_eventfd .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime /* * NOTE!! This doesn't have to be exact - we just have diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c index c1a3a2100ee..31ff35cff02 100644 --- a/arch/cris/arch-v10/kernel/fasttimer.c +++ b/arch/cris/arch-v10/kernel/fasttimer.c @@ -31,15 +31,12 @@ #define DEBUG_LOG_INCLUDED #define FAST_TIMER_LOG -//#define FAST_TIMER_TEST +/* #define FAST_TIMER_TEST */ #define FAST_TIMER_SANITY_CHECKS #ifdef FAST_TIMER_SANITY_CHECKS -#define SANITYCHECK(x) x static int sanity_failed; -#else -#define SANITYCHECK(x) #endif #define D1(x) @@ -226,23 +223,19 @@ void start_one_shot_timer(struct fast_timer *t, do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; - SANITYCHECK({ /* Check so this is not in the list already... */ - while (tmp != NULL) - { - if (tmp == t) - { - printk(KERN_WARNING - "timer name: %s data: 0x%08lX already in list!\n", name, data); - sanity_failed++; - goto done; - } - else - { - tmp = tmp->next; - } - } - tmp = fast_timer_list; - }); +#ifdef FAST_TIMER_SANITY_CHECKS + /* Check so this is not in the list already... */ + while (tmp != NULL) { + if (tmp == t) { + printk(KERN_WARNING "timer name: %s data: " + "0x%08lX already in list!\n", name, data); + sanity_failed++; + goto done; + } else + tmp = tmp->next; + } + tmp = fast_timer_list; +#endif t->delay_us = delay_us; t->function = function; diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S index d946d8b8d27..96344afc4eb 100644 --- a/arch/cris/arch-v10/kernel/head.S +++ b/arch/cris/arch-v10/kernel/head.S @@ -1,186 +1,10 @@ -/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $ - * +/* * Head of the kernel - alter with care * * Copyright (C) 2000, 2001 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: head.S,v $ - * Revision 1.10 2005/06/20 05:12:54 starvik - * Remove unnecessary diff to kernel.org tree - * - * Revision 1.9 2004/12/13 12:21:51 starvik - * Added I/O and DMA allocators from Linux 2.4 - * - * Revision 1.8 2004/11/22 11:41:14 starvik - * Kernel command line may be supplied to kernel. Not used by Axis but may - * be used by customers. - * - * Revision 1.7 2004/05/14 07:58:01 starvik - * Merge of changes from 2.4 - * - * Revision 1.6 2003/04/28 05:31:46 starvik - * Added section attributes - * - * Revision 1.5 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c - * - * Revision 1.4 2002/11/07 09:00:44 starvik - * Names changed for init sections - * init_task_union -> init_thread_union - * - * Revision 1.3 2002/02/05 15:38:23 bjornw - * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it... - * - * Revision 1.2 2001/12/18 13:35:19 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.43 2001/11/08 15:09:43 starvik - * Only start MII clock if Ethernet is configured - * - * Revision 1.42 2001/11/08 14:37:34 starvik - * Start MII clock early to make sure that it is running at tranceiver reset - * - * Revision 1.41 2001/10/29 14:55:58 pkj - * Corrected pa$r0 to par0. - * - * Revision 1.40 2001/10/03 14:59:57 pkj - * Added support for resetting the Bluetooth hardware. - * - * Revision 1.39 2001/10/01 14:45:03 bjornw - * Removed underscores and added register prefixes - * - * Revision 1.38 2001/09/21 07:14:11 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.37 2001/09/11 13:44:29 orjanf - * Decouple usage of serial ports for debug and kgdb. - * - * Revision 1.36 2001/06/29 12:39:31 pkj - * Added support for mirroring the first flash to just below the - * second one, to make them look consecutive to cramfs. - * - * Revision 1.35 2001/06/25 14:07:00 hp - * Fix review comment. - * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of - * magic numbers. Add comment that -traditional must not be used. - * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. - * Correct and update comment. - * * Makefile (.S.o): Don't use -traditional. Add comment why the - * toplevel rule can't be used (now that there's a reason). - * - * Revision 1.34 2001/05/15 07:08:14 hp - * Tweak "notice" to reflect that both r8 r9 are used - * - * Revision 1.33 2001/05/15 06:40:05 hp - * Put bulk of code in .text.init, data in .data.init - * - * Revision 1.32 2001/05/15 06:18:56 hp - * Execute review comment: s/bcc/bhs/g; s/bcs/blo/g - * - * Revision 1.31 2001/05/15 06:08:40 hp - * Add sentence about autodetecting the bit31-MMU-bug - * - * Revision 1.30 2001/05/15 06:00:05 hp - * Update comment: LOW_MAP is not forced on xsim anymore. - * - * Revision 1.29 2001/04/18 12:51:59 orjanf - * * Reverted review change regarding the use of bcs/bcc. - * * Removed non-working LED-clearing code. - * - * Revision 1.28 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.27 2001/04/17 11:42:35 orjanf - * Changed according to review: - * * Added comment explaining memory map bug. - * * Changed bcs and bcc to blo and bhs, respectively. - * * Removed mentioning of Stallone and Olga boards. - * - * Revision 1.26 2001/04/06 12:31:07 jonashg - * Check for cramfs in flash before RAM instead of RAM before flash. - * - * Revision 1.25 2001/04/04 06:23:53 starvik - * Initialize DRAM if not already initialized - * - * Revision 1.24 2001/04/03 11:12:00 starvik - * Removed dram init (done by rescue or etrax100boot - * Corrected include - * - * Revision 1.23 2001/04/03 09:53:03 starvik - * Include hw_settings.S - * - * Revision 1.22 2001/03/26 14:23:26 bjornw - * Namechange of some config options - * - * Revision 1.21 2001/03/08 12:14:41 bjornw - * * Config name for ETRAX IDE was renamed - * * Removed G27 auto-setting when JULIETTE is chosen (need to make this - * a new config option later) - * - * Revision 1.20 2001/02/23 12:47:56 bjornw - * MMU regs during LOW_MAP updated to reflect a newer reality - * - * Revision 1.19 2001/02/19 11:12:07 bjornw - * Changed comment header format - * - * Revision 1.18 2001/02/15 07:25:38 starvik - * Added support for synchronous serial ports - * - * Revision 1.17 2001/02/08 15:53:13 starvik - * Last commit removed some important ifdefs - * - * Revision 1.16 2001/02/08 15:20:38 starvik - * Include dram_init.S as inline - * - * Revision 1.15 2001/01/29 18:12:01 bjornw - * Corrected some comments - * - * Revision 1.14 2001/01/29 13:11:29 starvik - * Include dram_init.S (with DRAM/SDRAM initialization) - * - * Revision 1.13 2001/01/23 14:54:57 markusl - * Updated for USB - * i.e. added r_gen_config settings - * - * Revision 1.12 2001/01/19 16:16:29 perf - * Added temporary mapping of 0x0c->0x0c to avoid flash loading confusion. - * Renamed serial options from ETRAX100 to ETRAX. - * - * Revision 1.11 2001/01/16 16:31:38 bjornw - * * Changed name and semantics of running_from_flash to romfs_in_flash, - * set by head.S to indicate to setup.c whether there is a cramfs image - * after the kernels BSS or not. Should work for all three boot-cases - * (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot), - * and flash with cramfs in flash) - * - * Revision 1.10 2001/01/16 14:12:21 bjornw - * * Check for cramfs start passed in r9 from the decompressor, if all other - * cramfs options fail (if we boot from DRAM but don't find a cramfs image - * after the kernel in DRAM, it is probably still in the flash) - * * Check magic in cramfs detection when booting from flash directly - * - * Revision 1.9 2001/01/15 17:17:02 bjornw - * * Corrected the code that detects the cramfs lengths - * * Added a comment saying that the above does not work due to other - * reasons.. - * - * Revision 1.8 2001/01/15 16:27:51 jonashg - * Made boot after flashing work. - * * end destination is __vmlinux_end in RAM. - * * _romfs_start moved because of virtual memory. - * - * Revision 1.7 2000/11/21 13:55:29 bjornw - * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type - * - * Revision 1.6 2000/10/06 12:36:55 bjornw - * Forgot swapper_pg_dir when changing memory map.. - * - * Revision 1.5 2000/10/04 16:49:30 bjornw - * * Fixed memory mapping in LX - * * Check for cramfs instead of romfs - * */ #define ASSEMBLER_MACROS_ONLY @@ -595,11 +419,17 @@ no_command_line: moveq 0,$r0 + ;; Select or disable serial port 2 +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + or.d IO_STATE (R_GEN_CONFIG, ser2, select),$r0 +#else + or.d IO_STATE (R_GEN_CONFIG, ser2, disable),$r0 +#endif + ;; Init interfaces (disable them). or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \ | IO_STATE (R_GEN_CONFIG, ata, disable) \ | IO_STATE (R_GEN_CONFIG, par0, disable) \ - | IO_STATE (R_GEN_CONFIG, ser2, disable) \ | IO_STATE (R_GEN_CONFIG, mio, disable) \ | IO_STATE (R_GEN_CONFIG, scsi1, disable) \ | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \ @@ -801,6 +631,41 @@ no_command_line: | IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0 move.b $r0,[R_SERIAL1_TR_CTRL] +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + ;; setup the serial port 2 at 115200 baud for debug purposes + + moveq IO_STATE (R_SERIAL2_XOFF, tx_stop, enable) \ + | IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable) \ + | IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0 + move.d $r0,[R_SERIAL2_XOFF] + + ; 115.2kbaud for both transmit and receive + move.b IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz) \ + | IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0 + move.b $r0,[R_SERIAL2_BAUD] + + ; Set up and enable the serial2 receiver. + move.b IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rts_, active) \ + | IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0 + move.b $r0,[R_SERIAL2_REC_CTRL] + + ; Set up and enable the serial2 transmitter. + move.b IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled) \ + | IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0 + move.b $r0,[R_SERIAL2_TR_CTRL] +#endif #ifdef CONFIG_ETRAX_SERIAL_PORT3 ;; setup the serial port 3 at 115200 baud for debug purposes diff --git a/arch/cris/arch-v10/kernel/io_interface_mux.c b/arch/cris/arch-v10/kernel/io_interface_mux.c index f3b327d4ed9..add98e0941b 100644 --- a/arch/cris/arch-v10/kernel/io_interface_mux.c +++ b/arch/cris/arch-v10/kernel/io_interface_mux.c @@ -1,10 +1,9 @@ /* IO interface mux allocator for ETRAX100LX. - * Copyright 2004, Axis Communications AB - * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $ + * Copyright 2004-2007, Axis Communications AB */ -/* C.f. ETRAX100LX Designer's Reference 20.9 */ +/* C.f. ETRAX100LX Designer's Reference chapter 19.9 */ #include <linux/kernel.h> #include <linux/slab.h> @@ -45,17 +44,39 @@ struct watcher struct if_group { enum io_if_group group; - unsigned char used; - enum cris_io_interface owner; + /* name - the name of the group 'A' to 'F' */ + char *name; + /* used - a bit mask of all pins in the group in the order listed + * in the tables in 19.9.1 to 19.9.6. Note that no + * distinction is made between in, out and in/out pins. */ + unsigned int used; }; struct interface { enum cris_io_interface ioif; + /* name - the name of the interface */ + char *name; + /* groups - OR'ed together io_if_group flags describing what pin groups + * the interface uses pins in. */ unsigned char groups; + /* used - set when the interface is allocated. */ unsigned char used; char *owner; + /* group_a through group_f - bit masks describing what pins in the + * pin groups the interface uses. */ + unsigned int group_a; + unsigned int group_b; + unsigned int group_c; + unsigned int group_d; + unsigned int group_e; + unsigned int group_f; + + /* gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the + * GPIO ports the interface uses. This could be reconstucted using + * the group_X masks and a table of what pins the GPIO ports use, + * but that would be messy. */ unsigned int gpio_g_in; unsigned int gpio_g_out; unsigned char gpio_b; @@ -64,26 +85,32 @@ struct interface static struct if_group if_groups[6] = { { .group = group_a, + .name = "A", .used = 0, }, { .group = group_b, + .name = "B", .used = 0, }, { .group = group_c, + .name = "C", .used = 0, }, { .group = group_d, + .name = "D", .used = 0, }, { .group = group_e, + .name = "E", .used = 0, }, { .group = group_f, + .name = "F", .used = 0, } }; @@ -94,14 +121,32 @@ static struct interface interfaces[] = { /* Begin Non-multiplexed interfaces */ { .ioif = if_eth, + .name = "ethernet", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 }, { .ioif = if_serial_0, + .name = "serial_0", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 @@ -109,172 +154,385 @@ static struct interface interfaces[] = { /* End Non-multiplexed interfaces */ { .ioif = if_serial_1, + .name = "serial_1", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_serial_2, + .name = "serial_2", .groups = group_b, + + .group_a = 0, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_serial_3, + .name = "serial_3", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_sync_serial_1, - .groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3 - can be used simultaneously */ + .name = "sync_serial_1", + .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x10, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x10 }, { .ioif = if_sync_serial_3, + .name = "sync_serial_3", .groups = group_c | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x80 }, { .ioif = if_shared_ram, + .name = "shared_ram", .groups = group_a, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff38, .gpio_b = 0x00 }, { .ioif = if_shared_ram_w, + .name = "shared_ram_w", .groups = group_a | group_d, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff38, .gpio_b = 0x00 }, { .ioif = if_par_0, + .name = "par_0", .groups = group_a, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff3e, .gpio_b = 0x00 }, { .ioif = if_par_1, + .name = "par_1", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0x7feff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3eff0000, .gpio_g_out = 0x3eff0000, .gpio_b = 0x00 }, { .ioif = if_par_w, + .name = "par_w", .groups = group_a | group_d, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff3e, .gpio_b = 0x00 }, { .ioif = if_scsi8_0, - .groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_0", + .groups = group_a | group_b | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x10, + .gpio_g_in = 0x0000ffff, .gpio_g_out = 0x0000ffff, .gpio_b = 0x10 }, { .ioif = if_scsi8_1, - .groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_1", + .groups = group_c | group_d | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0x7ffff, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xffff0000, .gpio_g_out = 0xffff0000, .gpio_b = 0x80 }, { .ioif = if_scsi_w, + .name = "scsi_w", .groups = group_a | group_b | group_d | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0x601ff, + .group_e = 0, + .group_f = 0x90, + .gpio_g_in = 0x01ffffff, .gpio_g_out = 0x07ffffff, .gpio_b = 0x80 }, { .ioif = if_ata, + .name = "ata", .groups = group_a | group_b | group_c | group_d, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0x0f, + .group_d = 0x7cfff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xf9ffffff, .gpio_g_out = 0xffffffff, .gpio_b = 0x80 }, { .ioif = if_csp, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "csp", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0xfc, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xfc }, { .ioif = if_i2c, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "i2c", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x03, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x03 }, { .ioif = if_usb_1, + .name = "usb_1", .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x2c, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x2c }, { .ioif = if_usb_2, + .name = "usb_2", .groups = group_d, - .gpio_g_in = 0x0e000000, - .gpio_g_out = 0x3c000000, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x33e00, + .group_f = 0, + + .gpio_g_in = 0x3e000000, + .gpio_g_out = 0x0c000000, .gpio_b = 0x00 }, /* GPIO pins */ { .ioif = if_gpio_grp_a, + .name = "gpio_a", .groups = group_a, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3f, .gpio_g_out = 0x0000ff3f, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_b, + .name = "gpio_b", .groups = group_b, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_c, + .name = "gpio_c", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_d, + .name = "gpio_d", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3fff0000, .gpio_g_out = 0x3fff0000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_e, + .name = "gpio_e", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_f, + .name = "gpio_f", .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xff @@ -284,11 +542,13 @@ static struct interface interfaces[] = { static struct watcher *watchers = NULL; +/* The pins that are free to use in the GPIO ports. */ static unsigned int gpio_in_pins = 0xffffffff; static unsigned int gpio_out_pins = 0xffffffff; static unsigned char gpio_pb_pins = 0xff; static unsigned char gpio_pa_pins = 0xff; +/* Identifiers for the owners of the GPIO pins. */ static enum cris_io_interface gpio_pa_owners[8]; static enum cris_io_interface gpio_pb_owners[8]; static enum cris_io_interface gpio_pg_owners[32]; @@ -338,13 +598,15 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id struct if_group *grp; unsigned char group_set; unsigned long flags; + int res = 0; (void)cris_io_interface_init(); DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id)); if ((ioif >= if_max_interfaces) || (ioif < 0)) { - printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n", + printk(KERN_CRIT "cris_request_io_interface: Bad interface " + "%u submitted for %s\n", ioif, device_id); return -EINVAL; @@ -353,59 +615,69 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id local_irq_save(flags); if (interfaces[ioif].used) { - local_irq_restore(flags); - printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n", + printk(KERN_CRIT "cris_io_interface: Cannot allocate interface " + "%s for %s, in use by %s\n", + interfaces[ioif].name, device_id, interfaces[ioif].owner); - return -EBUSY; + res = -EBUSY; + goto exit; } - /* Check that all required groups are free before allocating, */ + /* Check that all required pins in the used groups are free + * before allocating. */ group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->used) { - if (grp->group == group_f) { - if ((if_sync_serial_1 == ioif) || - (if_sync_serial_3 == ioif)) { - if ((grp->owner != if_sync_serial_1) && - (grp->owner != if_sync_serial_3)) { - local_irq_restore(flags); - return -EBUSY; - } - } else if ((if_scsi8_0 == ioif) || - (if_scsi8_1 == ioif)) { - if ((grp->owner != if_scsi8_0) && - (grp->owner != if_scsi8_1)) { - local_irq_restore(flags); - return -EBUSY; - } - } - } else { - local_irq_restore(flags); - return -EBUSY; - } + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if (if_group_use & grp->used) { + printk(KERN_INFO "cris_request_io_interface: group " + "%s needed by %s not available\n", + grp->name, interfaces[ioif].name); + res = -EBUSY; + goto exit; + } + group_set = clear_group_from_set(group_set, grp); } /* Are the required GPIO pins available too? */ - if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) || - ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) || - ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) { - local_irq_restore(flags); - printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n", - ioif); - return -EBUSY; - } - - /* All needed I/O pins and pin groups are free, allocate. */ - group_set = interfaces[ioif].groups; - while (NULL != (grp = get_group(group_set))) { - grp->used = 1; - grp->owner = ioif; - group_set = clear_group_from_set(group_set, grp); + if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != + interfaces[ioif].gpio_g_in) || + ((interfaces[ioif].gpio_g_out & gpio_out_pins) != + interfaces[ioif].gpio_g_out) || + ((interfaces[ioif].gpio_b & gpio_pb_pins) != + interfaces[ioif].gpio_b)) { + printk(KERN_CRIT "cris_request_io_interface: Could not get " + "required pins for interface %u\n", ioif); + res = -EBUSY; + goto exit; } + /* Check which registers need to be reconfigured. */ gens = genconfig_shadow; gens_ii = gen_config_ii_shadow; @@ -495,9 +767,43 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id set_gen_config = 0; break; default: - panic("cris_request_io_interface: Bad interface %u submitted for %s\n", - ioif, - device_id); + printk(KERN_INFO "cris_request_io_interface: Bad interface " + "%u submitted for %s\n", + ioif, device_id); + res = -EBUSY; + goto exit; + } + + /* All needed I/O pins and pin groups are free, allocate. */ + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); + } + grp->used |= if_group_use; + + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 1; @@ -516,25 +822,28 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id *R_GEN_CONFIG_II = gen_config_ii_shadow; } - DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - gpio_in_pins, gpio_out_pins, gpio_pb_pins)); - DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - interfaces[ioif].gpio_g_in, - interfaces[ioif].gpio_g_out, - interfaces[ioif].gpio_b)); + DBG(printk(KERN_DEBUG "GPIO pins: available before: " + "g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk(KERN_DEBUG + "grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + interfaces[ioif].gpio_g_in, + interfaces[ioif].gpio_g_out, + interfaces[ioif].gpio_b)); gpio_in_pins &= ~interfaces[ioif].gpio_g_in; gpio_out_pins &= ~interfaces[ioif].gpio_g_out; gpio_pb_pins &= ~interfaces[ioif].gpio_b; - DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk(KERN_DEBUG "GPIO pins: available after: " + "g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); +exit: local_irq_restore(flags); - - notify_watchers(); - - return 0; + if (res == 0) + notify_watchers(); + return res; } @@ -560,43 +869,35 @@ void cris_free_io_interface(enum cris_io_interface ioif) } group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->group == group_f) { - switch (ioif) - { - case if_sync_serial_1: - if ((grp->owner == if_sync_serial_1) && - interfaces[if_sync_serial_3].used) { - grp->owner = if_sync_serial_3; - } else - grp->used = 0; - break; - case if_sync_serial_3: - if ((grp->owner == if_sync_serial_3) && - interfaces[if_sync_serial_1].used) { - grp->owner = if_sync_serial_1; - } else - grp->used = 0; - break; - case if_scsi8_0: - if ((grp->owner == if_scsi8_0) && - interfaces[if_scsi8_1].used) { - grp->owner = if_scsi8_1; - } else - grp->used = 0; - break; - case if_scsi8_1: - if ((grp->owner == if_scsi8_1) && - interfaces[if_scsi8_0].used) { - grp->owner = if_scsi8_0; - } else - grp->used = 0; - break; - default: - grp->used = 0; - } - } else { - grp->used = 0; + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if ((grp->used & if_group_use) != if_group_use) + BUG_ON(1); + grp->used = grp->used & ~if_group_use; + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 0; diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index e06ab0050d3..65ed803dae6 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -1,5 +1,4 @@ -/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $ - * +/* * linux/arch/cris/kernel/irq.c * * Copyright (c) 2000-2002 Axis Communications AB @@ -18,10 +17,6 @@ #include <linux/kernel.h> #include <linux/init.h> -/* From kgdb.c. */ -extern void kgdb_init(void); -extern void breakpoint(void); - #define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); #define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c index 77f4b142372..a3ca5515074 100644 --- a/arch/cris/arch-v10/kernel/kgdb.c +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -17,66 +17,8 @@ *! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'. *! Jul 21 1999 Bjorn Wesen eLinux port *! -*! $Log: kgdb.c,v $ -*! Revision 1.6 2005/01/14 10:12:17 starvik -*! KGDB on separate port. -*! Console fixes from 2.4. -*! -*! Revision 1.5 2004/10/07 13:59:08 starvik -*! Corrected call to set_int_vector -*! -*! Revision 1.4 2003/04/09 05:20:44 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.3 2003/01/21 19:11:08 starvik -*! Modified include path for new dir layout -*! -*! Revision 1.2 2002/11/19 14:35:24 starvik -*! Changes from linux 2.4 -*! Changed struct initializer syntax to the currently preferred notation -*! -*! Revision 1.1 2001/12/17 13:59:27 bjornw -*! Initial revision -*! -*! Revision 1.6 2001/10/09 13:10:03 matsfg -*! Added $ on registers and removed some underscores -*! -*! Revision 1.5 2001/04/17 13:58:39 orjanf -*! * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. -*! -*! Revision 1.4 2001/02/23 13:45:19 bjornw -*! config.h check -*! -*! Revision 1.3 2001/01/31 18:08:23 orjanf -*! Removed kgdb_handle_breakpoint from being the break 8 handler. -*! -*! Revision 1.2 2001/01/12 14:22:25 orjanf -*! Updated kernel debugging support to work with ETRAX 100LX. -*! -*! Revision 1.1 2000/07/10 16:25:21 bjornw -*! Initial revision -*! -*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw -*! * Initial version of arch/cris, the latest CRIS architecture with an MMU. -*! Mostly copied from arch/etrax100 with appropriate renames of files. -*! The mm/ subdir is copied from arch/i386. -*! This does not compile yet at all. -*! -*! -*! Revision 1.4 1999/07/22 17:25:25 bjornw -*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors. -*! -*! Revision 1.3 1999/07/21 19:51:18 bjornw -*! Check if the interrupting char is a ctrl-C, ignore otherwise. -*! -*! Revision 1.2 1999/07/21 18:09:39 bjornw -*! Ported to eLinux architecture, and added some kgdb documentation. -*! -*! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $ -*! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! *!**************************************************************************/ diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c index 1a3760c94f8..53117f07cc1 100644 --- a/arch/cris/arch-v10/kernel/process.c +++ b/arch/cris/arch-v10/kernel/process.c @@ -1,5 +1,4 @@ -/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $ - * +/* * linux/arch/cris/kernel/process.c * * Copyright (C) 1995 Linus Torvalds diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c index b570ae9b6ca..ee505b2eb4d 100644 --- a/arch/cris/arch-v10/kernel/ptrace.c +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -65,6 +65,7 @@ void ptrace_disable(struct task_struct *child) { /* Todo - pending singlesteps? */ + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } /* diff --git a/arch/cris/arch-v10/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c index 326178aef6e..2454a0b02f5 100644 --- a/arch/cris/arch-v10/kernel/shadows.c +++ b/arch/cris/arch-v10/kernel/shadows.c @@ -1,5 +1,4 @@ -/* $Id: shadows.c,v 1.2 2004/12/13 12:21:51 starvik Exp $ - * +/* * Various shadow registers. Defines for these are in include/asm-etrax100/io.h */ diff --git a/arch/cris/arch-v10/kernel/traps.c b/arch/cris/arch-v10/kernel/traps.c index 4becc1bcced..9eada5d8893 100644 --- a/arch/cris/arch-v10/kernel/traps.c +++ b/arch/cris/arch-v10/kernel/traps.c @@ -1,13 +1,10 @@ -/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $ +/* + * Helper functions for trap handlers * - * linux/arch/cris/arch-v10/traps.c + * Copyright (C) 2000-2007, Axis Communications AB. * - * Heler functions for trap handlers - * - * Copyright (C) 2000-2002 Axis Communications AB - * - * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Authors: Bjorn Wesen + * Hans-Peter Nilsson * */ @@ -15,124 +12,119 @@ #include <asm/uaccess.h> #include <asm/arch/sv_addr_ag.h> -extern int raw_printk(const char *fmt, ...); - -void -show_registers(struct pt_regs * regs) +void +show_registers(struct pt_regs *regs) { - /* We either use rdusp() - the USP register, which might not - correspond to the current process for all cases we're called, - or we use the current->thread.usp, which is not up to date for - the current process. Experience shows we want the USP - register. */ + /* + * It's possible to use either the USP register or current->thread.usp. + * USP might not correspond to the current process for all cases this + * function is called, and current->thread.usp isn't up to date for the + * current process. Experience shows that using USP is the way to go. + */ unsigned long usp = rdusp(); - raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", - regs->irp, regs->srp, regs->dccr, usp, regs->mof ); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp, regs->mof); + + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs); - raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); - raw_printk("Process %s (pid: %d, stackpage=%08lx)\n", + + printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs); + + printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); + + printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, (unsigned long)current); /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (! user_mode(regs)) { - int i; + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + int i; - show_stack(NULL, (unsigned long*)usp); + show_stack(NULL, (unsigned long *)usp); - /* Dump kernel stack if the previous dump wasn't one. */ + /* + * If the previous stack-dump wasn't a kernel one, dump the + * kernel stack now. + */ if (usp != 0) - show_stack (NULL, NULL); - - raw_printk("\nCode: "); - if(regs->irp < PAGE_OFFSET) - goto bad; - - /* Often enough the value at regs->irp does not point to - the interesting instruction, which is most often the - _previous_ instruction. So we dump at an offset large - enough that instruction decoding should be in sync at - the interesting point, but small enough to fit on a row - (sort of). We point out the regs->irp location in a - ksymoops-friendly way by wrapping the byte for that - address in parentheses. */ - for(i = -12; i < 12; i++) - { - unsigned char c; - if(__get_user(c, &((unsigned char*)regs->irp)[i])) { -bad: - raw_printk(" Bad IP value."); - break; - } + show_stack(NULL, NULL); + + printk("\nCode: "); + + if (regs->irp < PAGE_OFFSET) + goto bad_value; + + /* + * Quite often the value at regs->irp doesn't point to the + * interesting instruction, which often is the previous + * instruction. So dump at an offset large enough that the + * instruction decoding should be in sync at the interesting + * point, but small enough to fit on a row. The regs->irp + * location is pointed out in a ksymoops-friendly way by + * wrapping the byte for that address in parenthesises. + */ + for (i = -12; i < 12; i++) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->irp)[i])) { +bad_value: + printk(" Bad IP value."); + break; + } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); - } - raw_printk("\n"); - } + printk("%02x ", c); + } + printk("\n"); + } } -/* Called from entry.S when the watchdog has bitten - * We print out something resembling an oops dump, and if - * we have the nice doggy development flag set, we halt here - * instead of rebooting. - */ - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - - void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); - while(1) /* nothing */; -#else - show_registers(regs); -#endif + asm volatile ("setf m"); } -/* This is normally the 'Oops' routine */ -void -die_if_kernel(const char * str, struct pt_regs * regs, long err) +extern void (*nmi_handler)(struct pt_regs *); +void handle_nmi(struct pt_regs *regs) { - if(user_mode(regs)) - return; - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* This printout might take too long and trigger the - * watchdog normally. If we're in the nice doggy - * development mode, stop the watchdog during printout. - */ - stop_watchdog(); -#endif - - raw_printk("%s: %04lx\n", str, err & 0xffff); - - show_registers(regs); - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif - do_exit(SIGSEGV); + if (nmi_handler) + nmi_handler(regs); + + /* Wait until nmi is no longer active. (We enable NMI immediately after + returning from this function, and we don't want it happening while + exiting from the NMI interrupt handler.) */ + while (*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active)) + ; } -void arch_enable_nmi(void) +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) { - asm volatile("setf m"); + struct bug_frame f; + unsigned char c; + unsigned long irp = regs->irp; + + if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f)) + return; + if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC) + return; + if (__get_user(c, f.filename)) + f.filename = "<bad filename>"; + + printk("kernel BUG at %s:%d!\n", f.filename, f.line); } +#endif diff --git a/arch/cris/arch-v10/lib/checksum.S b/arch/cris/arch-v10/lib/checksum.S index 85c48f0a9ec..7d552f4bd5a 100644 --- a/arch/cris/arch-v10/lib/checksum.S +++ b/arch/cris/arch-v10/lib/checksum.S @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* * A fast checksum routine using movem * Copyright (c) 1998-2001 Axis Communications AB * @@ -61,8 +61,6 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords ax addq 0,$r12 - ax ; do it again, since we might have generated a carry - addq 0,$r12 subq 10*4,$r11 bge _mloop @@ -88,10 +86,6 @@ _word_loop: lsrq 16,$r13 ; r13 = checksum >> 16 and.d $r9,$r12 ; checksum = checksum & 0xffff add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 _no_fold: cmpq 2,$r11 diff --git a/arch/cris/arch-v10/lib/checksumcopy.S b/arch/cris/arch-v10/lib/checksumcopy.S index 35cbffb306f..540db8a5f84 100644 --- a/arch/cris/arch-v10/lib/checksumcopy.S +++ b/arch/cris/arch-v10/lib/checksumcopy.S @@ -1,4 +1,4 @@ -/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* * A fast checksum+copy routine using movem * Copyright (c) 1998, 2001 Axis Communications AB * @@ -67,8 +67,6 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords ax addq 0,$r13 - ax ; do it again, since we might have generated a carry - addq 0,$r13 subq 10*4,$r12 bge _mloop @@ -91,10 +89,6 @@ _word_loop: lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 _no_fold: cmpq 2,$r12 diff --git a/arch/cris/arch-v10/lib/dram_init.S b/arch/cris/arch-v10/lib/dram_init.S index 6a6bdfd6984..b9190ff7d0a 100644 --- a/arch/cris/arch-v10/lib/dram_init.S +++ b/arch/cris/arch-v10/lib/dram_init.S @@ -1,5 +1,4 @@ -/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $ - * +/* * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * @@ -8,60 +7,7 @@ * * Copyright (C) 2000, 2001 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) - * - * $Log: dram_init.S,v $ - * Revision 1.4 2003/09/22 09:21:59 starvik - * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx - * so we need to mask off 12 bits. - * - * Revision 1.3 2003/03/31 09:38:37 starvik - * Corrected calculation of end of sdram init commands - * - * Revision 1.2 2002/11/19 13:33:29 starvik - * Changes from Linux 2.4 - * - * Revision 1.13 2002/10/30 07:42:28 starvik - * Always read SDRAM command sequence from flash - * - * Revision 1.12 2002/08/09 11:37:37 orjanf - * Added double initialization work-around for Samsung SDRAMs. - * - * Revision 1.11 2002/06/04 11:43:21 starvik - * Check if mrs_data is specified in kernelconfig (necessary for MCM) - * - * Revision 1.10 2001/10/04 12:00:21 martinnn - * Added missing underscores. - * - * Revision 1.9 2001/10/01 14:47:35 bjornw - * Added register prefixes and removed underscores - * - * Revision 1.8 2001/05/15 07:12:45 hp - * Copy warning from head.S about r8 and r9 - * - * Revision 1.7 2001/04/18 12:05:39 bjornw - * Fixed comments, and explicitly include config.h to be sure its there - * - * Revision 1.6 2001/04/10 06:20:16 starvik - * Delay should be 200us, not 200ns - * - * Revision 1.5 2001/04/09 06:01:13 starvik - * Added support for 100 MHz SDRAMs - * - * Revision 1.4 2001/03/26 14:24:01 bjornw - * Namechange of some config options - * - * Revision 1.3 2001/03/23 08:29:41 starvik - * Corrected calculation of mrs_data - * - * Revision 1.2 2001/02/08 15:20:00 starvik - * Corrected SDRAM initialization - * Should now be included as inline - * - * Revision 1.1 2001/01/29 13:08:02 starvik - * Initial version - * This file should be included from all assembler files that needs to - * initialize DRAM/SDRAM. + * Authors: Mikael Starvik (starvik@axis.com) * */ diff --git a/arch/cris/arch-v10/lib/old_checksum.c b/arch/cris/arch-v10/lib/old_checksum.c index 497634a6482..1734b467efa 100644 --- a/arch/cris/arch-v10/lib/old_checksum.c +++ b/arch/cris/arch-v10/lib/old_checksum.c @@ -1,5 +1,4 @@ -/* $Id: old_checksum.c,v 1.3 2003/10/27 08:04:32 starvik Exp $ - * +/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c index fe2615022b9..65504fd8092 100644 --- a/arch/cris/arch-v10/mm/fault.c +++ b/arch/cris/arch-v10/mm/fault.c @@ -4,10 +4,10 @@ * Low level bus fault handler * * - * Copyright (C) 2000, 2001 Axis Communications AB + * Copyright (C) 2000-2007 Axis Communications AB + * + * Authors: Bjorn Wesen * - * Authors: Bjorn Wesen - * */ #include <linux/mm.h> @@ -60,7 +60,7 @@ handle_mmu_bus_fault(struct pt_regs *regs) #ifdef DEBUG page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); - inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); + inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); index = IO_EXTRACT(R_TLB_SELECT, index, select); #endif miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); @@ -84,12 +84,13 @@ handle_mmu_bus_fault(struct pt_regs *regs) local_irq_disable(); pmd = (pmd_t *)(pgd + pgd_index(address)); if (pmd_none(*pmd)) - return; + goto exit; pte = *pte_offset_kernel(pmd, address); if (!pte_present(pte)) - return; + goto exit; *R_TLB_SELECT = select; *R_TLB_HI = cause; *R_TLB_LO = pte_val(pte); +exit: local_irq_restore(flags); } diff --git a/arch/cris/arch-v10/mm/tlb.c b/arch/cris/arch-v10/mm/tlb.c index 7d9fec88dee..6baf5bd209e 100644 --- a/arch/cris/arch-v10/mm/tlb.c +++ b/arch/cris/arch-v10/mm/tlb.c @@ -4,8 +4,8 @@ * Low level TLB handling * * - * Copyright (C) 2000-2002 Axis Communications AB - * + * Copyright (C) 2000-2007 Axis Communications AB + * * Authors: Bjorn Wesen (bjornw@axis.com) * */ @@ -39,7 +39,7 @@ flush_tlb_all(void) unsigned long flags; /* the vpn of i & 0xf is so we dont write similar TLB entries - * in the same 4-way entry group. details.. + * in the same 4-way entry group. details... */ local_irq_save(flags); @@ -47,7 +47,7 @@ flush_tlb_all(void) *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -71,10 +71,10 @@ flush_tlb_mm(struct mm_struct *mm) if(page_id == NO_CONTEXT) return; - + /* mark the TLB entries that match the page_id as invalid. * here we could also check the _PAGE_GLOBAL bit and NOT flush - * global pages. is it worth the extra I/O ? + * global pages. is it worth the extra I/O ? */ local_irq_save(flags); @@ -83,7 +83,7 @@ flush_tlb_mm(struct mm_struct *mm) if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) { *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -96,9 +96,7 @@ flush_tlb_mm(struct mm_struct *mm) /* invalidate a single page */ -void -flush_tlb_page(struct vm_area_struct *vma, - unsigned long addr) +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { struct mm_struct *mm = vma->vm_mm; int page_id = mm->context.page_id; @@ -113,7 +111,7 @@ flush_tlb_page(struct vm_area_struct *vma, addr &= PAGE_MASK; /* perhaps not necessary */ /* invalidate those TLB entries that match both the mm context - * and the virtual address requested + * and the virtual address requested */ local_irq_save(flags); @@ -125,7 +123,7 @@ flush_tlb_page(struct vm_area_struct *vma, (tlb_hi & PAGE_MASK) == addr) { *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | addr; /* same addr as before works. */ - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -144,7 +142,7 @@ dump_tlb_all(void) { int i; unsigned long flags; - + printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we |\n"); local_save_flags(flags); @@ -172,27 +170,29 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm) /* called in schedule() just before actually doing the switch_to */ -void -switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk) +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) { - /* make sure we have a context */ + if (prev != next) { + /* make sure we have a context */ + get_mmu_context(next); - get_mmu_context(next); + /* remember the pgd for the fault handlers + * this is similar to the pgd register in some other CPU's. + * we need our own copy of it because current and active_mm + * might be invalid at points where we still need to derefer + * the pgd. + */ - /* remember the pgd for the fault handlers - * this is similar to the pgd register in some other CPU's. - * we need our own copy of it because current and active_mm - * might be invalid at points where we still need to derefer - * the pgd. - */ + per_cpu(current_pgd, smp_processor_id()) = next->pgd; - per_cpu(current_pgd, smp_processor_id()) = next->pgd; + /* switch context in the MMU */ - /* switch context in the MMU */ - - D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + D(printk(KERN_DEBUG "switching mmu_context to %d (%p)\n", + next->context, next)); - *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context.page_id); + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, + page_id, next->context.page_id); + } } diff --git a/arch/cris/arch-v32/Kconfig b/arch/cris/arch-v32/Kconfig index d8acaa920e1..005ed2b3f7f 100644 --- a/arch/cris/arch-v32/Kconfig +++ b/arch/cris/arch-v32/Kconfig @@ -1,27 +1,73 @@ if ETRAX_ARCH_V32 +source arch/cris/arch-v32/mach-fs/Kconfig +source arch/cris/arch-v32/mach-a3/Kconfig + +source drivers/cpufreq/Kconfig + config ETRAX_DRAM_VIRTUAL_BASE hex depends on ETRAX_ARCH_V32 default "c0000000" -config ETRAX_LED1G - string "First green LED bit" +choice + prompt "Nbr of Ethernet LED groups" depends on ETRAX_ARCH_V32 + default ETRAX_NBR_LED_GRP_ONE + help + Select how many Ethernet LED groups that can be used. Usually one per Ethernet + interface is a good choice. + +config ETRAX_NBR_LED_GRP_ZERO + bool "Use zero LED groups" + help + Select this if you do not want any Ethernet LEDs. + +config ETRAX_NBR_LED_GRP_ONE + bool "Use one LED group" + help + Select this if you want one Ethernet LED group. This LED group + can be used for one or more Ethernet interfaces. However, it is + recomended that each Ethernet interface use a dedicated LED group. + +config ETRAX_NBR_LED_GRP_TWO + bool "Use two LED groups" + help + Select this if you want two Ethernet LED groups. This is the + best choice if you have more than one Ethernet interface and + would like to have separate LEDs for the interfaces. + +endchoice + +config ETRAX_LED_G_NET0 + string "Ethernet LED group 0 green LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA3" help - Bit to use for the first green LED (network LED). - Most Axis products use bit A3 here. + Bit to use for the green LED in Ethernet LED group 0. -config ETRAX_LED1R - string "First red LED bit" - depends on ETRAX_ARCH_V32 +config ETRAX_LED_R_NET0 + string "Ethernet LED group 0 red LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA4" help - Bit to use for the first red LED (network LED). - Most Axis products use bit A4 here. + Bit to use for the red LED in Ethernet LED group 0. -config ETRAX_LED2G +config ETRAX_LED_G_NET1 + string "Ethernet group 1 green LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the green LED in Ethernet LED group 1. + +config ETRAX_LED_R_NET1 + string "Ethernet group 1 red LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the red LED in Ethernet LED group 1. + +config ETRAX_V32_LED2G string "Second green LED bit" depends on ETRAX_ARCH_V32 default "PA5" @@ -29,7 +75,7 @@ config ETRAX_LED2G Bit to use for the first green LED (status LED). Most Axis products use bit A5 here. -config ETRAX_LED2R +config ETRAX_V32_LED2R string "Second red LED bit" depends on ETRAX_ARCH_V32 default "PA6" @@ -37,7 +83,7 @@ config ETRAX_LED2R Bit to use for the first red LED (network LED). Most Axis products use bit A6 here. -config ETRAX_LED3G +config ETRAX_V32_LED3G string "Third green LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -45,7 +91,7 @@ config ETRAX_LED3G Bit to use for the first green LED (drive/power LED). Most Axis products use bit A7 here. -config ETRAX_LED3R +config ETRAX_V32_LED3R string "Third red LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -54,39 +100,6 @@ config ETRAX_LED3R Most Axis products use bit A7 here. choice - prompt "Product debug-port" - depends on ETRAX_ARCH_V32 - default ETRAX_DEBUG_PORT0 - -config ETRAX_DEBUG_PORT0 - bool "Serial-0" - help - Choose a serial port for the ETRAX debug console. Default to - port 0. - -config ETRAX_DEBUG_PORT1 - bool "Serial-1" - help - Use serial port 1 for the console. - -config ETRAX_DEBUG_PORT2 - bool "Serial-2" - help - Use serial port 2 for the console. - -config ETRAX_DEBUG_PORT3 - bool "Serial-3" - help - Use serial port 3 for the console. - -config ETRAX_DEBUG_PORT_NULL - bool "disabled" - help - Disable serial-port debugging. - -endchoice - -choice prompt "Kernel GDB port" depends on ETRAX_KGDB default ETRAX_KGDB_PORT0 @@ -95,25 +108,11 @@ choice not be enabled under Drivers for built-in interfaces (as it has its own initialization code) and should not be the same as the debug port. -config ETRAX_KGDB_PORT0 - bool "Serial-0" - help - Use serial port 0 for kernel debugging. - -config ETRAX_KGDB_PORT1 - bool "Serial-1" - help - Use serial port 1 for kernel debugging. - -config ETRAX_KGDB_PORT2 - bool "Serial-2" - help - Use serial port 2 for kernel debugging. - -config ETRAX_KGDB_PORT3 - bool "Serial-3" +config ETRAX_KGDB_PORT4 + bool "Serial-4" + depends on ETRAX_SERIAL_PORTS = 5 help - Use serial port 3 for kernel debugging. + Use serial port 4 for kernel debugging. endchoice diff --git a/arch/cris/arch-v32/boot/Makefile b/arch/cris/arch-v32/boot/Makefile index 26f293ab961..3f91349c5f1 100644 --- a/arch/cris/arch-v32/boot/Makefile +++ b/arch/cris/arch-v32/boot/Makefile @@ -1,14 +1,21 @@ # # arch/cris/arch-v32/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary -R .note -R .comment -compressed/vmlinuz: $(objtree)/vmlinux - @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - rm -f zImage tools/build compressed/vmlinux.out - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin + +$(obj)/zImage: $(obj)/compressed/vmlinux + @cp $< $@ + @echo ' Kernel: $@ is ready' diff --git a/arch/cris/arch-v32/boot/compressed/Makefile b/arch/cris/arch-v32/boot/compressed/Makefile index 609692f9d5e..2c8c2c3039c 100644 --- a/arch/cris/arch-v32/boot/compressed/Makefile +++ b/arch/cris/arch-v32/boot/compressed/Makefile @@ -1,41 +1,30 @@ # -# lx25/arch/cris/arch-v32/boot/compressed/Makefile +# arch/cris/arch-v32/boot/compressed/Makefile # -# create a compressed vmlinux image from the original vmlinux files and romfs -# - -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +asflags-y += -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch +ccflags-y += -O2 -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/decompress.ld +obj-y = head.o misc.o +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o - -# files to compress -SYSTEM = $(objtree)/vmlinux.bin - -all: vmlinuz - -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin -$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz - rm -f piggy.img - cp $(objtree)/vmlinuz $(src) +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $@ +targets := vmlinux piggy.gz decompress.o decompress.bin -# gzip the kernel image +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -piggy.img: $(SYSTEM) - cat $(SYSTEM) | gzip -f -9 > piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -clean: - rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS) +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff --git a/arch/cris/arch-v32/boot/compressed/README b/arch/cris/arch-v32/boot/compressed/README index e33691d15c5..182c5d75784 100644 --- a/arch/cris/arch-v32/boot/compressed/README +++ b/arch/cris/arch-v32/boot/compressed/README @@ -1,6 +1,5 @@ Creation of the self-extracting compressed kernel image (vmlinuz) ----------------------------------------------------------------- -$Id: README,v 1.1 2003/08/21 09:37:03 johana Exp $ This can be slightly confusing because it's a process with many steps. diff --git a/arch/cris/arch-v32/boot/compressed/head.S b/arch/cris/arch-v32/boot/compressed/head.S index 34cea10a899..f86208caf32 100644 --- a/arch/cris/arch-v32/boot/compressed/head.S +++ b/arch/cris/arch-v32/boot/compressed/head.S @@ -2,13 +2,12 @@ * Code that sets up the DRAM registers, calls the * decompressor to unpack the piggybacked kernel, and jumps. * - * Copyright (C) 1999 - 2003, Axis Communications AB + * Copyright (C) 1999 - 2006, Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/gio_defs_asm.h> -#include <asm/arch/hwregs/asm/config_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 @@ -22,114 +21,49 @@ start: di ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] - - ;; If booting from NAND flash we first have to copy some - ;; data from NAND flash to internal RAM to get the code - ;; that initializes the SDRAM. Lets copy 20 KB. This - ;; code executes at 0x38010000 if booting from NAND and - ;; we are guaranted that at least 0x200 bytes are good so - ;; lets start from there. The first 8192 bytes in the nand - ;; flash is spliced with zeroes and is thus 16384 bytes. - move.d 0x38010200, $r10 - move.d 0x14200, $r11 ; Start offset in NAND flash 0x10200 + 16384 - move.d 0x5000, $r12 ; Length of copy - - ;; Before this code the tools add a partitiontable so the PC - ;; has an offset from the linked address. -offset1: - lapcq ., $r13 ; get PC - add.d first_copy_complete-offset1, $r13 - -#include "../../lib/nand_init.S" - -first_copy_complete: - ;; Initialze the DRAM registers. + START_CLOCKS + + ;; Initialize the DRAM registers. cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? beq dram_init_finished nop -#include "../../lib/dram_init.S" +#include "../../mach/dram_init.S" dram_init_finished: - lapcq ., $r13 ; get PC - add.d second_copy_complete-dram_init_finished, $r13 - - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne second_copy_complete ; No NAND boot - nop - - ;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM) - move.d 0x40204000, $r10 - move.d 0x8000, $r11 - move.d 0x200000, $r12 - ba copy_nand_to_ram - nop -second_copy_complete: - - ;; Initiate the PA port. - move.d CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0 - move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r1 - move.d $r0, [$r1] - - move.d CONFIG_ETRAX_DEF_GIO_PA_OE, $r0 - move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r1 - move.d $r0, [$r1] + GIO_INIT ;; Setup the stack to a suitably high address. ;; We assume 8 MB is the minimum DRAM and put ;; the SP at the top for now. move.d 0x40800000, $sp - ;; Figure out where the compressed piggyback image is - ;; in the flash (since we wont try to copy it to DRAM - ;; before unpacking). It is at _edata, but in flash. + ;; Figure out where the compressed piggyback image is. + ;; It is either in [NOR] flash (we don't want to copy it + ;; to DRAM before unpacking), or copied to DRAM + ;; by the [NAND] flash boot loader. + ;; The piggyback image is at _edata, but relative to where the + ;; image is actually located in memory, not where it is linked + ;; (the decompressor is linked at 0x40700000+ and runs there). ;; Use (_edata - herami) as offset to the current PC. - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - beq hereami2 - nop hereami: lapcq ., $r5 ; get PC and.d 0x7fffffff, $r5 ; strip any non-cache bit - move.d $r5, $r0 ; save for later - flash address of 'herami' + move.d $r5, $r0 ; source address of 'herami' add.d _edata, $r5 sub.d hereami, $r5 ; r5 = flash address of '_edata' move.d hereami, $r1 ; destination - ba 2f - nop -hereami2: - lapcq ., $r5 ; get PC - and.d 0x00ffffff, $r5 ; strip any non-cache bit - move.d $r5, $r6 - or.d 0x40200000, $r6 - move.d $r6, $r0 ; save for later - flash address of 'herami' - add.d _edata, $r5 - sub.d hereami2, $r5 ; r5 = flash address of '_edata' - add.d 0x40200000, $r5 - move.d hereami2, $r1 ; destination -2: + ;; Copy text+data to DRAM move.d _edata, $r2 ; end destination -1: move.w [$r0+], $r3 - move.w $r3, [$r1+] - cmp.d $r2, $r1 +1: move.w [$r0+], $r3 ; from herami+ source + move.w $r3, [$r1+] ; to hereami+ destination (linked address) + cmp.d $r2, $r1 ; finish when destination == _edata bcs 1b nop - move.d input_data, $r0 ; for the decompressor move.d $r5, [$r0] ; for the decompressor @@ -144,16 +78,24 @@ hereami2: nop ;; Save command line magic and address. - move.d _cmd_line_magic, $r12 - move.d $r10, [$r12] - move.d _cmd_line_addr, $r12 - move.d $r11, [$r12] + move.d _cmd_line_magic, $r0 + move.d $r10, [$r0] + move.d _cmd_line_addr, $r0 + move.d $r11, [$r0] + + ;; Save boot source indicator + move.d _boot_source, $r0 + move.d $r12, [$r0] ;; Do the decompression and save compressed size in _inptr jsr decompress_kernel nop + ;; Restore boot source indicator + move.d _boot_source, $r12 + move.d [$r12], $r12 + ;; Restore command line magic and address. move.d _cmd_line_magic, $r10 move.d [$r10], $r10 @@ -166,11 +108,10 @@ hereami2: move.d [$r0], $r9 ; flash address of compressed kernel move.d inptr, $r0 add.d [$r0], $r9 ; size of compressed kernel - cmp.d 0x40200000, $r9 - blo enter_kernel - nop - sub.d 0x40200000, $r9 - add.d 0x4000, $r9 + cmp.d 0x40000000, $r9 ; image in DRAM ? + blo enter_kernel ; no, must be [NOR] flash, jump + nop ; delay slot + and.d 0x001fffff, $r9 ; assume compressed kernel was < 2M enter_kernel: ;; Enter the decompressed kernel @@ -186,7 +127,7 @@ _cmd_line_magic: .dword 0 _cmd_line_addr: .dword 0 -is_nand_boot: - .dword 0 +_boot_source: + .dword 0 -#include "../../lib/hw_settings.S" +#include "../../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/boot/compressed/misc.c b/arch/cris/arch-v32/boot/compressed/misc.c index 0169ba1ca9c..55b2695c5d7 100644 --- a/arch/cris/arch-v32/boot/compressed/misc.c +++ b/arch/cris/arch-v32/boot/compressed/misc.c @@ -1,8 +1,6 @@ /* * misc.c * - * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $ - * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * @@ -22,9 +20,13 @@ #include <linux/types.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/ser_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/pinmux_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* * gzip declarations @@ -85,7 +87,6 @@ static unsigned outcnt = 0; /* bytes in output buffer */ # define Tracecv(c,x) #endif -static int fill_inbuf(void); static void flush_window(void); static void error(char *m); static void gzip_mark(void **); @@ -186,6 +187,8 @@ memset(void* s, int c, size_t n) char *ss = (char*)s; for (i=0;i<n;i++) ss[i] = c; + + return s; } void* @@ -196,6 +199,8 @@ memcpy(void* __dest, __const void* __src, char *d = (char *)__dest, *s = (char *)__src; for (i=0;i<__n;i++) d[i] = s[i]; + + return __dest; } /* =========================================================================== @@ -225,15 +230,15 @@ flush_window() static void error(char *x) { - puts("\n\n"); + puts("\r\n\n"); puts(x); - puts("\n\n -- System halted\n"); + puts("\r\n\n -- System halted\n"); while(1); /* Halt */ } void -setup_normal_output_buffer() +setup_normal_output_buffer(void) { output_data = (char *)KERNEL_LOAD_ADR; } @@ -262,15 +267,17 @@ serial_setup(reg_scope_instances regi_ser) rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div); tr_ctrl.stop_bits = 1; /* 2 stop bits. */ + tr_ctrl.en = 1; /* enable transmitter */ + rec_ctrl.en = 1; /* enabler receiver */ /* - * The baudrate setup is a bit fishy, but in the end the transceiver is - * set to 4800 and the receiver to 115200. The magic value is - * 29.493 MHz. + * The baudrate setup used to be a bit fishy, but now transmitter and + * receiver are both set to the intended baud rate, 115200. + * The magic value is 29.493 MHz. */ tr_ctrl.base_freq = regk_ser_f29_493; rec_ctrl.base_freq = regk_ser_f29_493; - tr_baud.div = (29493000 / 8) / 4800; + tr_baud.div = (29493000 / 8) / 115200; rec_baud.div = (29493000 / 8) / 115200; REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); @@ -280,25 +287,52 @@ serial_setup(reg_scope_instances regi_ser) } void -decompress_kernel() +decompress_kernel(void) { char revision; - /* input_data is set in head.S */ - inbuf = input_data; +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + reg_pinmux_rw_hwprot hwprot; + +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + reg_clkgen_rw_clk_ctrl clk_ctrl; + + /* Enable corresponding clock region when serial 1..3 selected */ + + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); +#endif + + /* pinmux setup for ports 1..3 */ + hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); +#endif #ifdef CONFIG_ETRAX_DEBUG_PORT0 serial_setup(regi_ser0); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT1 + hwprot.ser1 = regk_pinmux_yes; serial_setup(regi_ser1); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT2 + hwprot.ser2 = regk_pinmux_yes; serial_setup(regi_ser2); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT3 + hwprot.ser3 = regk_pinmux_yes; serial_setup(regi_ser3); #endif +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); +#endif + + /* input_data is set in head.S */ + inbuf = input_data; setup_normal_output_buffer(); @@ -307,11 +341,11 @@ decompress_kernel() __asm__ volatile ("move $vr,%0" : "=rm" (revision)); if (revision < 32) { - puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n"); + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n"); while(1); } - puts("Uncompressing Linux...\n"); + puts("Uncompressing Linux...\r\n"); gunzip(); - puts("Done. Now booting the kernel.\n"); + puts("Done. Now booting the kernel.\r\n"); } diff --git a/arch/cris/arch-v32/boot/rescue/Makefile b/arch/cris/arch-v32/boot/rescue/Makefile index f668a819872..c0987795dcb 100644 --- a/arch/cris/arch-v32/boot/rescue/Makefile +++ b/arch/cris/arch-v32/boot/rescue/Makefile @@ -1,36 +1,27 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 -I $(srctree)/include/asm/arch/mach/ \ + -I $(srctree)/include/asm/arch +asflags-y += -I $(srctree)/include/asm/arch/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/rescue.ld +LDPOSTFLAGS = -lgcc OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-$(CONFIG_ETRAX_AXISFLASHMAP) = head.o +OBJECT := $(obj)/head.o -all: $(target)/rescue.bin +targets := rescue.o rescue.bin -rescue: rescue.bin - # do nothing +quiet_cmd_ldlibgcc = LD $@ +cmd_ldlibgcc = $(LD) $(LDFLAGS) $(filter-out FORCE,$^) $(LDPOSTFLAGS) -o $@ -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin - cp -p $(target)/rescue.bin $(objtree) +$(obj)/rescue.o: $(OBJECTS) FORCE + $(call if_changed,ldlibgcc) -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) diff --git a/arch/cris/arch-v32/boot/rescue/head.S b/arch/cris/arch-v32/boot/rescue/head.S index 8cdb4011bc1..5f846b7700a 100644 --- a/arch/cris/arch-v32/boot/rescue/head.S +++ b/arch/cris/arch-v32/boot/rescue/head.S @@ -1,38 +1,26 @@ -/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $ +/* + * Just get started by jumping to CONFIG_ETRAX_PTABLE_SECTOR to start + * kernel decompressor. + * + * In practice, this only works for NOR flash (or some convoluted RAM boot) + * and hence is not really useful for Artpec-3, so it's Etrax FS / NOR only. * - * This used to be the rescue code but now that is handled by the - * RedBoot based RFL instead. Nothing to see here, move along. */ -#include <asm/arch/hwregs/reg_map_asm.h> -#include <asm/arch/hwregs/config_defs_asm.h> +#include <mach/startup.inc> - .text +#ifdef CONFIG_ETRAX_AXISFLASHMAP - ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] +;; Code - ;; Copy 68KB NAND flash to Internal RAM (if NAND boot) - move.d 0x38004000, $r10 - move.d 0x8000, $r11 - move.d 0x11000, $r12 - move.d copy_complete, $r13 - and.d 0x000fffff, $r13 - or.d 0x38000000, $r13 + .text +start: -#include "../../lib/nand_init.S" + ;; Start clocks for used blocks. + START_CLOCKS - ;; No NAND found move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser + jump $r10 ; Jump to decompressor nop -copy_complete: - move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser - nop +#endif diff --git a/arch/cris/arch-v32/boot/rescue/rescue.ld b/arch/cris/arch-v32/boot/rescue/rescue.ld index 42b11aa122b..8ac646bc1a2 100644 --- a/arch/cris/arch-v32/boot/rescue/rescue.ld +++ b/arch/cris/arch-v32/boot/rescue/rescue.ld @@ -1,20 +1,43 @@ +/*#OUTPUT_FORMAT(elf32-us-cris) */ +OUTPUT_ARCH (crisv32) +/* Now that NAND support has been stripped, this file could be simplified, + * but it doesn't do any harm on the other hand so why bother. */ + MEMORY { - flash : ORIGIN = 0x00000000, - LENGTH = 0x00100000 + bootblk : ORIGIN = 0x38000000, + LENGTH = 0x00004000 + intmem : ORIGIN = 0x38004000, + LENGTH = 0x00005000 } SECTIONS { .text : { - stext = . ; + _stext = . ; *(.text) - etext = . ; - } > flash + *(.init.text) + *(.rodata) + *(.rodata.*) + _etext = . ; + } > bootblk .data : { *(.data) - edata = . ; - } > flash + _edata = . ; + } > bootblk + .bss : + { + _bss = . ; + *(.bss) + _end = ALIGN( 0x10 ) ; + } > intmem + + /* Get rid of stuff from EXPORT_SYMBOL(foo). */ + /DISCARD/ : + { + *(__ksymtab_strings) + *(__ksymtab) + } } diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index c329cce2a0c..2a92cb1886c 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -4,64 +4,102 @@ config ETRAX_ETHERNET bool "Ethernet support" depends on ETRAX_ARCH_V32 select NET_ETHERNET + select MII help This option enables the ETRAX FS built-in 10/100Mbit Ethernet controller. -config ETRAX_ETHERNET_HW_CSUM - bool "Hardware accelerated ethernet checksum and scatter/gather" +config ETRAX_NO_PHY + bool "PHY not present" depends on ETRAX_ETHERNET - depends on ETRAX_STREAMCOPROC - default y + default N help - Hardware acceleration of checksumming and scatter/gather + This option disables all MDIO communication with an ethernet + transceiver connected to the MII interface. This option shall + typically be enabled if the MII interface is connected to a + switch. This option should normally be disabled. If enabled, + speed and duplex will be locked to 100 Mbit and full duplex. config ETRAX_ETHERNET_IFACE0 depends on ETRAX_ETHERNET bool "Enable network interface 0" config ETRAX_ETHERNET_IFACE1 - depends on ETRAX_ETHERNET + depends on (ETRAX_ETHERNET && ETRAXFS) bool "Enable network interface 1 (uses DMA6 and DMA7)" +config ETRAX_ETHERNET_GBIT + depends on (ETRAX_ETHERNET && CRIS_MACH_ARTPEC3) + bool "Enable gigabit Ethernet support" + choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + prompt "Eth0 led group" + depends on ETRAX_ETHERNET_IFACE0 + default ETRAX_ETH0_USE_LEDGRP0 -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" +config ETRAX_ETH0_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use LED grp 0 for eth0 - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. +config ETRAX_ETH0_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth0 -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" +config ETRAX_ETH0_USE_LEDGRPNULL + bool "Use no LEDs for eth0" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use no LEDs for eth0 +endchoice - Selecting LED_on_when_activity will light the LED only when - there is activity. +choice + prompt "Eth1 led group" + depends on ETRAX_ETHERNET_IFACE1 + default ETRAX_ETH1_USE_LEDGRP1 + +config ETRAX_ETH1_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 0 for eth1 - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. +config ETRAX_ETH1_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth1 +config ETRAX_ETH1_USE_LEDGRPNULL + bool "Use no LEDs for eth1" + help + Use no LEDs for eth1 endchoice config ETRAXFS_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V32 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE help Enables the ETRAX FS serial driver for ser0 (ttyS0) You probably want this enabled. +config ETRAX_RS485 + bool "RS-485 support" + depends on ETRAXFS_SERIAL + help + Enables support for RS-485 serial communication. + +config ETRAX_RS485_DISABLE_RECEIVER + bool "Disable serial receiver" + depends on ETRAX_RS485 + help + It is necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + config ETRAX_SERIAL_PORT0 bool "Serial port 0 enabled" depends on ETRAXFS_SERIAL @@ -72,50 +110,28 @@ config ETRAX_SERIAL_PORT0 ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. choice - prompt "Ser0 DMA in channel " + prompt "Ser0 default port type " depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_NO_DMA_IN + default ETRAX_SERIAL_PORT0_TYPE_232 help - What DMA channel to use for ser0. - + Type of serial port. -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "Ser0 uses no DMA for input" +config ETRAX_SERIAL_PORT0_TYPE_232 + bool "Ser0 is a RS-232 port" help - Do not use DMA for ser0 input. + Configure serial port 0 to be a RS-232 port. -config ETRAX_SERIAL_PORT0_DMA7_IN - bool "Ser0 uses DMA7 for input" - depends on ETRAX_SERIAL_PORT0 - help - Enables the DMA7 input channel for ser0 (ttyS0). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - -endchoice - -choice - prompt "Ser0 DMA out channel" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_NO_DMA_OUT - -config ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "Ser0 uses no DMA for output" +config ETRAX_SERIAL_PORT0_TYPE_485HD + bool "Ser0 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser0 output. + Configure serial port 0 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "Ser0 uses DMA6 for output" - depends on ETRAX_SERIAL_PORT0 +config ETRAX_SERIAL_PORT0_TYPE_485FD + bool "Ser0 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA6 output channel for ser0 (ttyS0). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - + Configure serial port 0 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER0_DTR_BIT @@ -141,52 +157,28 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX FS serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA in channel " + prompt "Ser1 default port type" depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_NO_DMA_IN - help - What DMA channel to use for ser1. - - -config ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "Ser1 uses no DMA for input" + default ETRAX_SERIAL_PORT1_TYPE_232 help - Do not use DMA for ser1 input. + Type of serial port. -config ETRAX_SERIAL_PORT1_DMA5_IN - bool "Ser1 uses DMA5 for input" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_232 + bool "Ser1 is a RS-232 port" help - Enables the DMA5 input channel for ser1 (ttyS1). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want this on, unless you use the DMA channel for - something else. - -endchoice + Configure serial port 1 to be a RS-232 port. -choice - prompt "Ser1 DMA out channel " - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_NO_DMA_OUT - help - What DMA channel to use for ser1. - -config ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "Ser1 uses no DMA for output" +config ETRAX_SERIAL_PORT1_TYPE_485HD + bool "Ser1 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser1 output. + Configure serial port 1 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT1_DMA4_OUT - bool "Ser1 uses DMA4 for output" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_485FD + bool "Ser1 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA4 output channel for ser1 (ttyS1). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want this on, unless you use the DMA channel for - something else. - + Configure serial port 1 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER1_DTR_BIT @@ -212,52 +204,31 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX FS serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA in channel " + prompt "Ser2 default port type" depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_NO_DMA_IN + default ETRAX_SERIAL_PORT2_TYPE_232 help - What DMA channel to use for ser2. + What DMA channel to use for ser2 - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "Ser2 uses no DMA for input" +config ETRAX_SERIAL_PORT2_TYPE_232 + bool "Ser2 is a RS-232 port" help - Do not use DMA for ser2 input. + Configure serial port 2 to be a RS-232 port. -config ETRAX_SERIAL_PORT2_DMA3_IN - bool "Ser2 uses DMA3 for input" - depends on ETRAX_SERIAL_PORT2 - help - Enables the DMA3 input channel for ser2 (ttyS2). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - -endchoice - -choice - prompt "Ser2 DMA out channel" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_NO_DMA_OUT - -config ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "Ser2 uses no DMA for output" +config ETRAX_SERIAL_PORT2_TYPE_485HD + bool "Ser2 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser2 output. + Configure serial port 2 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "Ser2 uses DMA2 for output" - depends on ETRAX_SERIAL_PORT2 +config ETRAX_SERIAL_PORT2_TYPE_485FD + bool "Ser2 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA2 output channel for ser2 (ttyS2). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - + Configure serial port 2 to be a full duplex (four wires) RS-485 port. endchoice + config ETRAX_SER2_DTR_BIT string "Ser 2 DTR bit (empty = not used)" depends on ETRAX_SERIAL_PORT2 @@ -281,71 +252,121 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX FS serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA in channel " + prompt "Ser3 default port type" depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_IN + default ETRAX_SERIAL_PORT3_TYPE_232 help What DMA channel to use for ser3. +config ETRAX_SERIAL_PORT3_TYPE_232 + bool "Ser3 is a RS-232 port" + help + Configure serial port 3 to be a RS-232 port. -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "Ser3 uses no DMA for input" +config ETRAX_SERIAL_PORT3_TYPE_485HD + bool "Ser3 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser3 input. + Configure serial port 3 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT3_DMA9_IN - bool "Ser3 uses DMA9 for input" +config ETRAX_SERIAL_PORT3_TYPE_485FD + bool "Ser3 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 3 to be a full duplex (four wires) RS-485 port. +endchoice + +config ETRAX_SER3_DTR_BIT + string "Ser 3 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_RI_BIT + string "Ser 3 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_DSR_BIT + string "Ser 3 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_CD_BIT + string "Ser 3 CD bit (empty = not used)" depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SERIAL_PORT4 + bool "Serial port 4 enabled" + depends on ETRAXFS_SERIAL && CRIS_MACH_ARTPEC3 help - Enables the DMA9 input channel for ser3 (ttyS3). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. + Enables the ETRAX FS serial driver for ser4 (ttyS4). + +choice + prompt "Ser4 default port type" + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_TYPE_232 + help + What DMA channel to use for ser4. +config ETRAX_SERIAL_PORT4_TYPE_232 + bool "Ser4 is a RS-232 port" + help + Configure serial port 4 to be a RS-232 port. + +config ETRAX_SERIAL_PORT4_TYPE_485HD + bool "Ser4 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT4_TYPE_485FD + bool "Ser4 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a full duplex (four wires) RS-485 port. endchoice choice - prompt "Ser3 DMA out channel" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_OUT + prompt "Ser4 DMA in channel " + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_NO_DMA_IN + help + What DMA channel to use for ser4. + -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "Ser3 uses no DMA for output" +config ETRAX_SERIAL_PORT4_NO_DMA_IN + bool "Ser4 uses no DMA for input" help - Do not use DMA for ser3 output. + Do not use DMA for ser4 input. -config ETRAX_SERIAL_PORT3_DMA8_OUT - bool "Ser3 uses DMA8 for output" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SERIAL_PORT4_DMA9_IN + bool "Ser4 uses DMA9 for input" + depends on ETRAX_SERIAL_PORT4 help - Enables the DMA8 output channel for ser3 (ttyS3). + Enables the DMA9 input channel for ser4 (ttyS4). If you do not enable DMA, an interrupt for each character will be - used when transmitting data. + used when receiveing data. Normally you want to use DMA, unless you use the DMA channel for something else. endchoice -config ETRAX_SER3_DTR_BIT - string "Ser 3 DTR bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_DTR_BIT + string "Ser 4 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_RI_BIT - string "Ser 3 RI bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_RI_BIT + string "Ser 4 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_DSR_BIT - string "Ser 3 DSR bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_DSR_BIT + string "Ser 4 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_SER3_CD_BIT - string "Ser 3 CD bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 + string "Ser 4 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_RS485 bool "RS-485 support" - depends on ETRAX_SERIAL + depends on ETRAXFS_SERIAL help Enables support for RS-485 serial communication. For a primer on RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>. @@ -356,22 +377,6 @@ config ETRAX_RS485_DISABLE_RECEIVER help It is necessary to disable the serial receiver to avoid serial loopback. Not all products are able to do this in software only. - Axis 2400/2401 must disable receiver. - -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V32 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. config ETRAX_SYNCHRONOUS_SERIAL bool "Synchronous serial-port support" @@ -394,7 +399,7 @@ config ETRAX_SYNCHRONOUS_SERIAL0_DMA config ETRAX_SYNCHRONOUS_SERIAL_PORT1 bool "Synchronous serial port 1 enabled" - depends on ETRAX_SYNCHRONOUS_SERIAL + depends on ETRAX_SYNCHRONOUS_SERIAL && ETRAXFS help Enabled synchronous serial port 1. @@ -405,6 +410,31 @@ config ETRAX_SYNCHRONOUS_SERIAL1_DMA A synchronous serial port can run in manual or DMA mode. Selecting this option will make it run in DMA mode. +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V32 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_AXISFLASHMAP_MTD0WHOLE + bool "MTD0 is whole boot flash device" + depends on ETRAX_AXISFLASHMAP + default N + help + When this option is not set, mtd0 refers to the first partition + on the boot flash device. When set, mtd0 refers to the whole + device, with mtd1 referring to the first partition etc. + config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -425,42 +455,32 @@ config ETRAX_NANDFLASH This option enables MTD mapping of NAND flash devices. Needed to use NAND flash memories. If unsure, say Y. +config ETRAX_NANDBOOT + bool "Boot from NAND flash" + depends on ETRAX_NANDFLASH + help + This options enables booting from NAND flash devices. + Say Y if your boot code, kernel and root file system is in + NAND flash. Say N if they are in NOR flash. + config ETRAX_I2C bool "I2C driver" depends on ETRAX_ARCH_V32 help - This option enabled the I2C driver used by e.g. the RTC driver. + This option enables the I2C driver used by e.g. the RTC driver. -config ETRAX_I2C_DATA_PORT +config ETRAX_V32_I2C_DATA_PORT string "I2C data pin" depends on ETRAX_I2C help The pin to use for I2C data. -config ETRAX_I2C_CLK_PORT +config ETRAX_V32_I2C_CLK_PORT string "I2C clock pin" depends on ETRAX_I2C help The pin to use for I2C clock. -config ETRAX_RTC - bool "Real Time Clock support" - depends on ETRAX_ARCH_V32 - help - Enabled RTC support. - -choice - prompt "RTC chip" - depends on ETRAX_RTC - default ETRAX_PCF8563 - -config ETRAX_PCF8563 - bool "PCF8563" - help - Philips PCF8563 RTC - -endchoice - config ETRAX_GPIO bool "GPIO support" depends on ETRAX_ARCH_V32 @@ -476,33 +496,36 @@ config ETRAX_GPIO Remember that you need to setup the port directions appropriately in the General configuration. -config ETRAX_PA_BUTTON_BITMASK - hex "PA-buttons bitmask" +config ETRAX_VIRTUAL_GPIO + bool "Virtual GPIO support" depends on ETRAX_GPIO - default "0x02" help - This is a bitmask (8 bits) with information about what bits on PA - that are used for buttons. - Most products has a so called TEST button on PA1, if that is true - use 0x02 here. - Use 00 if there are no buttons on PA. - If the bitmask is <> 00 a button driver will be included in the gpio - driver. ETRAX general I/O support must be enabled. + Enables the virtual Etrax general port device (major 120, minor 6). + It uses an I/O expander for the I2C-bus. + +config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN + int "Virtual GPIO interrupt pin on PA pin" + range 0 7 + depends on ETRAX_VIRTUAL_GPIO + help + The pin to use on PA for virtual gpio interrupt. config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (8 bits) with information of what bits in PA that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PA_CHANGEABLE_BITS hex "PA user changeable bits mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (8 bits) with information of what bits in PA that a user can change the value on using ioctl's. @@ -511,17 +534,19 @@ config ETRAX_PA_CHANGEABLE_BITS config ETRAX_PB_CHANGEABLE_DIR hex "PB user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PB that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00000 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PB_CHANGEABLE_BITS hex "PB user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PB that a user can change the value on using ioctl's. @@ -530,17 +555,19 @@ config ETRAX_PB_CHANGEABLE_BITS config ETRAX_PC_CHANGEABLE_DIR hex "PC user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PC that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00000 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PC_CHANGEABLE_BITS hex "PC user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if ETRAXFS help This is a bitmask (18 bits) with information of what bits in PC that a user can change the value on using ioctl's. @@ -548,7 +575,7 @@ config ETRAX_PC_CHANGEABLE_BITS config ETRAX_PD_CHANGEABLE_DIR hex "PD user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -558,7 +585,7 @@ config ETRAX_PD_CHANGEABLE_DIR config ETRAX_PD_CHANGEABLE_BITS hex "PD user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -567,7 +594,7 @@ config ETRAX_PD_CHANGEABLE_BITS config ETRAX_PE_CHANGEABLE_DIR hex "PE user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PE @@ -577,20 +604,36 @@ config ETRAX_PE_CHANGEABLE_DIR config ETRAX_PE_CHANGEABLE_BITS hex "PE user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PE that a user can change the value on using ioctl's. Bit set = changeable. +config ETRAX_PV_CHANGEABLE_DIR + hex "PV user changeable dir mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x0000 here, but it depends on your hardware. + +config ETRAX_PV_CHANGEABLE_BITS + hex "PV user changeable bits mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change the value on using ioctl's. + Bit set = changeable. + config ETRAX_CARDBUS bool "Cardbus support" depends on ETRAX_ARCH_V32 - select PCCARD - select CARDBUS select HOTPLUG - select PCCARD_NONSTATIC help Enabled the ETRAX Cardbus driver. @@ -613,4 +656,202 @@ config ETRAX_STREAMCOPROC This option enables a driver for the stream co-processor for cryptographic operations. +source drivers/mmc/Kconfig + +config ETRAX_MMC_IOP + tristate "MMC/SD host driver using IO-processor" + depends on ETRAX_ARCH_V32 && MMC + help + This option enables the SD/MMC host controller interface. + The host controller is implemented using the built in + IO-Processor. Only the SPU is used in this implementation. + +config ETRAX_SPI_MMC +# Make this one of several "choices" (possible simultaneously but +# suggested uniquely) when an IOP driver emerges for "real" MMC/SD +# protocol support. + tristate + depends on !ETRAX_MMC_IOP + default MMC + select SPI + select MMC_SPI + select ETRAX_SPI_MMC_BOARD + +# For the parts that can't be a module (due to restrictions in +# framework elsewhere). +config ETRAX_SPI_MMC_BOARD + boolean + default n + +# While the board info is MMC_SPI only, the drivers are written to be +# independent of MMC_SPI, so we'll keep SPI non-dependent on the +# MMC_SPI config choices (well, except for a single depends-on-line +# for the board-info file until a separate non-MMC SPI board file +# emerges). +# FIXME: When that happens, we'll need to be able to ask for and +# configure non-MMC SPI ports together with MMC_SPI ports (if multiple +# SPI ports are enabled). + +config SPI_ETRAX_SSER + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using an synchronous serial (sser) port as a + SPI master controller on Axis ETRAX FS and later. The + driver can be configured to use any sser port. + +config SPI_ETRAX_GPIO + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using GPIO pins port as a SPI master controller + on Axis ETRAX FS and later. The driver can be configured to + use any GPIO pins. + +config ETRAX_SPI_SSER0 + tristate "SPI using synchronous serial port 0 (sser0)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m + default y if MMC_SPI=y + default y if MMC_SPI=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 0, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER0_DMA + bool "DMA for SPI on sser0 enabled" + depends on ETRAX_SPI_SSER0 + depends on !ETRAX_SERIAL_PORT1_DMA4_OUT && !ETRAX_SERIAL_PORT1_DMA5_IN + default y + help + Say Y if using DMA (dma4/dma5) for SPI on synchronous serial port 0. + +config ETRAX_SPI_MMC_CD_SSER0_PIN + string "MMC/SD card detect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER0_PIN + string "MMC/SD card write-protect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_SSER1 + tristate "SPI using synchronous serial port 1 (sser1)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 1, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER1_DMA + bool "DMA for SPI on sser1 enabled" + depends on ETRAX_SPI_SSER1 && !ETRAX_ETHERNET_IFACE1 + depends on !ETRAX_SERIAL_PORT0_DMA6_OUT && !ETRAX_SERIAL_PORT0_DMA7_IN + default y + help + Say Y if using DMA (dma6/dma7) for SPI on synchronous serial port 1. + +config ETRAX_SPI_MMC_CD_SSER1_PIN + string "MMC/SD card detect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd12" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER1_PIN + string "MMC/SD card write-protect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd9" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_GPIO + tristate "Bitbanged SPI using gpio pins" + depends on ETRAX_SPI_MMC + select SPI_ETRAX_GPIO + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + help + Say Y for an MMC/SD socket connected to general I/O pins (but not + a complete synchronous serial ports), or for devices using the SPI + protocol on general I/O pins. Slow and slows down the system. + Say m to build it as a module, which will be called spi_crisv32_gpio. + (You need to select MMC separately.) + +# The default match that of sser0, only because that's how it was tested. +config ETRAX_SPI_CS_PIN + string "SPI chip select pin" + depends on ETRAX_SPI_GPIO + default "pc3" + help + The pin to use for SPI chip select. + +config ETRAX_SPI_CLK_PIN + string "SPI clock pin" + depends on ETRAX_SPI_GPIO + default "pc1" + help + The pin to use for the SPI clock. + +config ETRAX_SPI_DATAIN_PIN + string "SPI MISO (data in) pin" + depends on ETRAX_SPI_GPIO + default "pc16" + help + The pin to use for SPI data in from the device. + +config ETRAX_SPI_DATAOUT_PIN + string "SPI MOSI (data out) pin" + depends on ETRAX_SPI_GPIO + default "pc0" + help + The pin to use for SPI data out to the device. + +config ETRAX_SPI_MMC_CD_GPIO_PIN + string "MMC/SD card detect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_GPIO_PIN + string "MMC/SD card write-protect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +# Avoid choices causing non-working configs by conditionalizing the inclusion. +if ETRAX_SPI_MMC +source drivers/spi/Kconfig +endif + endif diff --git a/arch/cris/arch-v32/drivers/Makefile b/arch/cris/arch-v32/drivers/Makefile index a359cd20ae7..e8c02437eda 100644 --- a/arch/cris/arch-v32/drivers/Makefile +++ b/arch/cris/arch-v32/drivers/Makefile @@ -4,10 +4,11 @@ obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o -obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o -obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAXFS) += mach-fs/ +obj-$(CONFIG_CRIS_MACH_ARTPEC3) += mach-a3/ obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o obj-$(CONFIG_ETRAX_I2C) += i2c.o obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o obj-$(CONFIG_PCI) += pci/ +obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o diff --git a/arch/cris/arch-v32/drivers/axisflashmap.c b/arch/cris/arch-v32/drivers/axisflashmap.c index c5ff95e1826..51e1e85df96 100644 --- a/arch/cris/arch-v32/drivers/axisflashmap.c +++ b/arch/cris/arch-v32/drivers/axisflashmap.c @@ -1,7 +1,7 @@ /* * Physical mapping layer for MTD using the Axis partitiontable format * - * Copyright (c) 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 2001-2007 Axis Communications AB * * This file is under the GPL. * @@ -10,9 +10,6 @@ * tells us what other partitions to define. If there isn't, we use a default * partition split defined below. * - * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5 - * with minor changes. - * */ #include <linux/module.h> @@ -27,7 +24,8 @@ #include <linux/mtd/mtdram.h> #include <linux/mtd/partitions.h> -#include <asm/arch/hwregs/config_defs.h> +#include <linux/cramfs_fs.h> + #include <asm/axisflashmap.h> #include <asm/mmu.h> @@ -37,16 +35,24 @@ #define FLASH_UNCACHED_ADDR KSEG_E #define FLASH_CACHED_ADDR KSEG_F +#define PAGESIZE (512) + #if CONFIG_ETRAX_FLASH_BUSWIDTH==1 #define flash_data __u8 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 #define flash_data __u16 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 -#define flash_data __u16 +#define flash_data __u32 #endif /* From head.S */ -extern unsigned long romfs_start, romfs_length, romfs_in_flash; +extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */ +extern unsigned long romfs_start, romfs_length; +extern unsigned long nand_boot; /* 1 when booted from nand flash */ + +struct partition_name { + char name[6]; +}; /* The master mtd for the entire flash. */ struct mtd_info* axisflash_mtd = NULL; @@ -112,32 +118,20 @@ static struct map_info map_cse1 = { .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE }; -/* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 +#define MAX_PARTITIONS 7 +#ifdef CONFIG_ETRAX_NANDBOOT +#define NUM_DEFAULT_PARTITIONS 4 +#define DEFAULT_ROOTFS_PARTITION_NO 2 +#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */ +#else +#define NUM_DEFAULT_PARTITIONS 3 +#define DEFAULT_ROOTFS_PARTITION_NO (-1) +#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */ +#endif -/* - * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the - * size of one flash block and "filesystem"-partition needs 5 blocks to be able - * to use JFFS. - */ -static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { - { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0 - }, - { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR - }, - { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) - } -}; +#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS) +#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS +#endif /* Initialize the ones normally used. */ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { @@ -178,6 +172,56 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; + +/* If no partition-table was found, we use this default-set. + * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most + * likely the size of one flash block and "filesystem"-partition needs + * to be >=5 blocks to be able to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, +#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR) +#ifdef CONFIG_ETRAX_NANDBOOT + { + .name = "rootfs", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = FILESYSTEM_SECTOR + }, +#undef FILESYSTEM_SECTOR +#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR) +#endif + { + .name = "rwfs", + .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR, + .offset = FILESYSTEM_SECTOR + } +}; + +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + +/* Auxilliary partition if we find another flash */ +static struct mtd_partition aux_partition = { + .name = "aux", + .size = 0, + .offset = 0 +}; + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -191,7 +235,7 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) map_cs->name, map_cs->size, map_cs->map_priv_1); #ifdef CONFIG_MTD_CFI - mtd_cs = do_map_probe("cfi_probe", map_cs); + mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE if (!mtd_cs) @@ -204,7 +248,7 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) /* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct - * so that MTD partitions can cross chip boundaries. + * so that MTD partitions can cross chip boundries. * * The only known restriction to how you can mount your chips is that each * chip select must hold similar flash chips. But you need external hardware @@ -216,9 +260,8 @@ static struct mtd_info *flash_probe(void) { struct mtd_info *mtd_cse0; struct mtd_info *mtd_cse1; - struct mtd_info *mtd_nand = NULL; struct mtd_info *mtd_total; - struct mtd_info *mtds[3]; + struct mtd_info *mtds[2]; int count = 0; if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL) @@ -226,12 +269,7 @@ static struct mtd_info *flash_probe(void) if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL) mtds[count++] = mtd_cse1; -#ifdef CONFIG_ETRAX_NANDFLASH - if ((mtd_nand = crisv32_nand_flash_probe()) != NULL) - mtds[count++] = mtd_nand; -#endif - - if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) { + if (!mtd_cse0 && !mtd_cse1) { /* No chip found. */ return NULL; } @@ -245,9 +283,7 @@ static struct mtd_info *flash_probe(void) * So we use the MTD concatenation layer instead of further * complicating the probing procedure. */ - mtd_total = mtd_concat_create(mtds, - count, - "cse0+cse1+nand"); + mtd_total = mtd_concat_create(mtds, count, "cse0+cse1"); #else printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " "(mis)configuration!\n", map_cse0.name, map_cse1.name); @@ -255,61 +291,162 @@ static struct mtd_info *flash_probe(void) #endif if (!mtd_total) { printk(KERN_ERR "%s and %s: Concatenation failed!\n", - map_cse0.name, map_cse1.name); + map_cse0.name, map_cse1.name); /* The best we can do now is to only use what we found - * at cse0. - */ + * at cse0. */ mtd_total = mtd_cse0; map_destroy(mtd_cse1); } - } else { - mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand; - - } + } else + mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1; return mtd_total; } -extern unsigned long crisv32_nand_boot; -extern unsigned long crisv32_nand_cramfs_offset; - /* * Probe the flash chip(s) and, if it succeeds, read the partition-table * and register the partitions with MTD. */ static int __init init_axis_flash(void) { - struct mtd_info *mymtd; + struct mtd_info *main_mtd; + struct mtd_info *aux_mtd = NULL; int err = 0; int pidx = 0; struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; - int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n"; - static char page[512]; + int ptable_ok = 0; + static char page[PAGESIZE]; size_t len; + int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */ + int part; + + /* We need a root fs. If it resides in RAM, we need to use an + * MTDRAM device, so it must be enabled in the kernel config, + * but its size must be configured as 0 so as not to conflict + * with our usage. + */ +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + if (!romfs_in_flash && !nand_boot) { + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device; configure CONFIG_MTD_MTDRAM with size = 0!\n"); + panic("This kernel cannot boot from RAM!\n"); + } +#endif + +#ifndef CONFIG_ETRAX_VCS_SIM + main_mtd = flash_probe(); + if (main_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NOR flash memory.\n", + main_mtd->name, main_mtd->size); + +#ifdef CONFIG_ETRAX_NANDFLASH + aux_mtd = crisv32_nand_flash_probe(); + if (aux_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n", + aux_mtd->name, aux_mtd->size); + +#ifdef CONFIG_ETRAX_NANDBOOT + { + struct mtd_info *tmp_mtd; -#ifndef CONFIG_ETRAXFS_SIM - mymtd = flash_probe(); - mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page); - ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET); + printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, " + "making NAND flash primary device.\n"); + tmp_mtd = main_mtd; + main_mtd = aux_mtd; + aux_mtd = tmp_mtd; + } +#endif /* CONFIG_ETRAX_NANDBOOT */ +#endif /* CONFIG_ETRAX_NANDFLASH */ - if (!mymtd) { + if (!main_mtd && !aux_mtd) { /* There's no reason to use this module if no flash chip can * be identified. Make sure that's understood. */ printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); - } else { - printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", - mymtd->name, mymtd->size); - axisflash_mtd = mymtd; } - if (mymtd) { - mymtd->owner = THIS_MODULE; +#if 0 /* Dump flash memory so we can see what is going on */ + if (main_mtd) { + int sectoraddr, i; + for (sectoraddr = 0; sectoraddr < 2*65536+4096; + sectoraddr += PAGESIZE) { + main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len, + page); + printk(KERN_INFO + "Sector at %d (length %d):\n", + sectoraddr, len); + for (i = 0; i < PAGESIZE; i += 16) { + printk(KERN_INFO + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + page[i] & 255, page[i+1] & 255, + page[i+2] & 255, page[i+3] & 255, + page[i+4] & 255, page[i+5] & 255, + page[i+6] & 255, page[i+7] & 255, + page[i+8] & 255, page[i+9] & 255, + page[i+10] & 255, page[i+11] & 255, + page[i+12] & 255, page[i+13] & 255, + page[i+14] & 255, page[i+15] & 255); + } + } + } +#endif + + if (main_mtd) { + main_mtd->owner = THIS_MODULE; + axisflash_mtd = main_mtd; + + loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR; + + /* First partition (rescue) is always set to the default. */ + pidx++; +#ifdef CONFIG_ETRAX_NANDBOOT + /* We know where the partition table should be located, + * it will be in first good block after that. + */ + int blockstat; + do { + blockstat = main_mtd->block_isbad(main_mtd, + ptable_sector); + if (blockstat < 0) + ptable_sector = 0; /* read error */ + else if (blockstat) + ptable_sector += main_mtd->erasesize; + } while (blockstat && ptable_sector); +#endif + if (ptable_sector) { + main_mtd->read(main_mtd, ptable_sector, PAGESIZE, + &len, page); + ptable_head = &((struct partitiontable *) page)->head; + } + +#if 0 /* Dump partition table so we can see what is going on */ + printk(KERN_INFO + "axisflashmap: flash read %d bytes at 0x%08x, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + len, CONFIG_ETRAX_PTABLE_SECTOR, + page[0] & 255, page[1] & 255, + page[2] & 255, page[3] & 255, + page[4] & 255, page[5] & 255, + page[6] & 255, page[7] & 255); + printk(KERN_INFO + "axisflashmap: partition table offset %d, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + PARTITION_TABLE_OFFSET, + page[PARTITION_TABLE_OFFSET+0] & 255, + page[PARTITION_TABLE_OFFSET+1] & 255, + page[PARTITION_TABLE_OFFSET+2] & 255, + page[PARTITION_TABLE_OFFSET+3] & 255, + page[PARTITION_TABLE_OFFSET+4] & 255, + page[PARTITION_TABLE_OFFSET+5] & 255, + page[PARTITION_TABLE_OFFSET+6] & 255, + page[PARTITION_TABLE_OFFSET+7] & 255); +#endif } - pidx++; /* First partition is always set to the default. */ if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) && (ptable_head->size < @@ -322,7 +459,6 @@ static int __init init_axis_flash(void) /* Looks like a start, sane length and end of a * partition table, lets check csum etc. */ - int ptable_ok = 0; struct partitiontable_entry *max_addr = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head) + @@ -346,104 +482,170 @@ static int __init init_axis_flash(void) ptable_ok = (csum == ptable_head->checksum); /* Read the entries and use/show the info. */ - printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + printk(KERN_INFO "axisflashmap: " + "Found a%s partition table at 0x%p-0x%p.\n", (ptable_ok ? " valid" : "n invalid"), ptable_head, max_addr); /* We have found a working bootblock. Now read the - * partition table. Scan the table. It ends when - * there is 0xffffffff, that is, empty flash. + * partition table. Scan the table. It ends with 0xffffffff. */ while (ptable_ok - && ptable->offset != 0xffffffff + && ptable->offset != PARTITIONTABLE_END_MARKER && ptable < max_addr - && pidx < MAX_PARTITIONS) { + && pidx < MAX_PARTITIONS - 1) { - axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0); - axis_partitions[pidx].size = ptable->size; - - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + axis_partitions[pidx].offset = offset + ptable->offset; +#ifdef CONFIG_ETRAX_NANDFLASH + if (main_mtd->type == MTD_NANDFLASH) { + axis_partitions[pidx].size = + (((ptable+1)->offset == + PARTITIONTABLE_END_MARKER) ? + main_mtd->size : + ((ptable+1)->offset + offset)) - + (ptable->offset + offset); + + } else +#endif /* CONFIG_ETRAX_NANDFLASH */ + axis_partitions[pidx].size = ptable->size; +#ifdef CONFIG_ETRAX_NANDBOOT + /* Save partition number of jffs2 ro partition. + * Needed if RAM booting or root file system in RAM. + */ + if (!nand_boot && + ram_rootfs_partition < 0 && /* not already set */ + ptable->type == PARTITION_TYPE_JFFS2 && + (ptable->flags & PARTITION_FLAGS_READONLY_MASK) == + PARTITION_FLAGS_READONLY) + ram_rootfs_partition = pidx; +#endif /* CONFIG_ETRAX_NANDBOOT */ pidx++; ptable++; } - use_default_ptable = !ptable_ok; } - if (romfs_in_flash) { - /* Add an overlapping device for the root partition (romfs). */ + /* Decide whether to use default partition table. */ + /* Only use default table if we actually have a device (main_mtd) */ - axis_partitions[pidx].name = "romfs"; - if (crisv32_nand_boot) { - char* data = kmalloc(1024, GFP_KERNEL); - int len; - int offset = crisv32_nand_cramfs_offset & ~(1024-1); - char* tmp; - - mymtd->read(mymtd, offset, 1024, &len, data); - tmp = &data[crisv32_nand_cramfs_offset % 512]; - axis_partitions[pidx].size = *(unsigned*)(tmp + 4); - axis_partitions[pidx].offset = crisv32_nand_cramfs_offset; - kfree(data); - } else { - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; - } + struct mtd_partition *partition = &axis_partitions[0]; + if (main_mtd && !ptable_ok) { + memcpy(axis_partitions, axis_default_partitions, + sizeof(axis_default_partitions)); + pidx = NUM_DEFAULT_PARTITIONS; + ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO; + } + /* Add artificial partitions for rootfs if necessary */ + if (romfs_in_flash) { + /* rootfs is in directly accessible flash memory = NOR flash. + Add an overlapping device for the rootfs partition. */ + printk(KERN_INFO "axisflashmap: Adding partition for " + "overlapping root file system image\n"); + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].name = "romfs"; axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - - printk(KERN_INFO - " Adding readonly flash partition for romfs image:\n"); - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + ram_rootfs_partition = -1; pidx++; - } - - if (mymtd) { - if (use_default_ptable) { - printk(KERN_INFO " Using default partition table.\n"); - err = add_mtd_partitions(mymtd, axis_default_partitions, - NUM_DEFAULT_PARTITIONS); - } else { - err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } else if (romfs_length && !nand_boot) { + /* romfs exists in memory, but not in flash, so must be in RAM. + * Configure an MTDRAM partition. */ + if (ram_rootfs_partition < 0) { + /* None set yet, put it at the end */ + ram_rootfs_partition = pidx; + pidx++; } + printk(KERN_INFO "axisflashmap: Adding partition for " + "root file system image in RAM\n"); + axis_partitions[ram_rootfs_partition].size = romfs_length; + axis_partitions[ram_rootfs_partition].offset = romfs_start; + axis_partitions[ram_rootfs_partition].name = "romfs"; + axis_partitions[ram_rootfs_partition].mask_flags |= + MTD_WRITEABLE; + } - if (err) { - panic("axisflashmap could not add MTD partitions!\n"); - } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (main_mtd) { + main_partition.size = main_mtd->size; + err = add_mtd_partitions(main_mtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); } -/* CONFIG_EXTRAXFS_SIM */ #endif - if (!romfs_in_flash) { - /* Create an RAM device for the root partition (romfs). */ + /* Now, register all partitions with mtd. + * We do this one at a time so we can slip in an MTDRAM device + * in the proper place if required. */ + + for (part = 0; part < pidx; part++) { + if (part == ram_rootfs_partition) { + /* add MTDRAM partition here */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) + panic("axisflashmap: Couldn't allocate memory " + "for mtd_info!\n"); + printk(KERN_INFO "axisflashmap: Adding RAM partition " + "for rootfs image.\n"); + err = mtdram_init_device(mtd_ram, + (void *)partition[part].offset, + partition[part].size, + partition[part].name); + if (err) + panic("axisflashmap: Could not initialize " + "MTD RAM device!\n"); + /* JFFS2 likes to have an erasesize. Keep potential + * JFFS2 rootfs happy by providing one. Since image + * was most likely created for main mtd, use that + * erasesize, if available. Otherwise, make a guess. */ + mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize : + CONFIG_ETRAX_PTABLE_SECTOR); + } else { + err = add_mtd_partitions(main_mtd, &partition[part], 1); + if (err) + panic("axisflashmap: Could not add mtd " + "partition %d\n", part); + } + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ + +#ifdef CONFIG_ETRAX_VCS_SIM + /* For simulator, always use a RAM partition. + * The rootfs will be found after the kernel in RAM, + * with romfs_start and romfs_end indicating location and size. + */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap: Couldn't allocate memory for " + "mtd_info!\n"); + } -#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) - /* No use trying to boot this kernel from RAM. Panic! */ - printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " - "device due to kernel (mis)configuration!\n"); - panic("This kernel cannot boot from RAM!\n"); -#else - struct mtd_info *mtd_ram; + printk(KERN_INFO "axisflashmap: Adding RAM partition for romfs, " + "at %u, size %u\n", + (unsigned) romfs_start, (unsigned) romfs_length); - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { - panic("axisflashmap couldn't allocate memory for " - "mtd_info!\n"); - } + err = mtdram_init_device(mtd_ram, (void *)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap: Could not initialize MTD RAM " + "device!\n"); + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ - printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); +#ifndef CONFIG_ETRAX_VCS_SIM + if (aux_mtd) { + aux_partition.size = aux_mtd->size; + err = add_mtd_partitions(aux_mtd, &aux_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "aux mtd device!\n"); - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { - panic("axisflashmap could not initialize MTD RAM " - "device!\n"); - } -#endif } +#endif /* CONFIG_EXTRAX_VCS_SIM */ return err; } diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c index e8914d40169..9fb58202be9 100644 --- a/arch/cris/arch-v32/drivers/cryptocop.c +++ b/arch/cris/arch-v32/drivers/cryptocop.c @@ -1,8 +1,7 @@ -/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $ - * +/* * Stream co-processor driver for the ETRAX FS * - * Copyright (C) 2003-2005 Axis Communications AB + * Copyright (C) 2003-2007 Axis Communications AB */ #include <linux/init.h> @@ -25,17 +24,29 @@ #include <asm/signal.h> #include <asm/irq.h> -#include <asm/arch/dma.h> -#include <asm/arch/hwregs/dma.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/intr_vect_defs.h> - -#include <asm/arch/hwregs/strcop.h> -#include <asm/arch/hwregs/strcop_defs.h> -#include <asm/arch/cryptocop.h> - - +#include <dma.h> +#include <hwregs/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect_defs.h> + +#include <hwregs/strcop.h> +#include <hwregs/strcop_defs.h> +#include <cryptocop.h> + +#ifdef CONFIG_ETRAXFS +#define IN_DMA 9 +#define OUT_DMA 8 +#define IN_DMA_INST regi_dma9 +#define OUT_DMA_INST regi_dma8 +#define DMA_IRQ DMA9_INTR_VECT +#else +#define IN_DMA 3 +#define OUT_DMA 2 +#define IN_DMA_INST regi_dma3 +#define OUT_DMA_INST regi_dma2 +#define DMA_IRQ DMA3_INTR_VECT +#endif #define DESCR_ALLOC_PAD (31) @@ -1886,14 +1897,14 @@ static void cryptocop_do_tasklet(unsigned long unused) } static irqreturn_t -dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) +dma_done_interrupt(int irq, void *dev_id) { struct cryptocop_prio_job *done_job; reg_dma_rw_ack_intr ack_intr = { .data = 1, }; - REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); DEBUG(printk("cryptocop DMA done\n")); @@ -1937,7 +1948,6 @@ dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) static int init_cryptocop(void) { unsigned long flags; - reg_intr_vect_rw_mask intr_mask; reg_dma_rw_cfg dma_cfg = {.en = 1}; reg_dma_rw_intr_mask intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */ reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; @@ -1950,10 +1960,14 @@ static int init_cryptocop(void) .en = 1 }; - if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9"); + if (request_irq(DMA_IRQ, dma_done_interrupt, 0, + "stream co-processor DMA", NULL)) + panic("request_irq stream co-processor irq dma9"); - (void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); - (void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); + (void)crisv32_request_dma(OUT_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); + (void)crisv32_request_dma(IN_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); local_irq_save(flags); @@ -1963,24 +1977,19 @@ static int init_cryptocop(void) strcop_cfg.en = 1; REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg); - /* Enable DMA9 interrupt */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.dma9 = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - /* Enable DMAs. */ - REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ - REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */ /* Set up wordsize = 4 for DMAs. */ - DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4); - DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4); + DMA_WR_CMD(OUT_DMA_INST, regk_dma_set_w_size4); + DMA_WR_CMD(IN_DMA_INST, regk_dma_set_w_size4); /* Enable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); /* Clear intr ack. */ - REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); local_irq_restore(flags); @@ -1991,7 +2000,6 @@ static int init_cryptocop(void) static void release_cryptocop(void) { unsigned long flags; - reg_intr_vect_rw_mask intr_mask; reg_dma_rw_cfg dma_cfg = {.en = 0}; reg_dma_rw_intr_mask intr_mask_in = {0}; reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; @@ -1999,26 +2007,21 @@ static void release_cryptocop(void) local_irq_save(flags); /* Clear intr ack. */ - REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); - - /* Disable DMA9 interrupt */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.dma9 = 0; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); /* Disable DMAs. */ - REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ - REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */ /* Disable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); local_irq_restore(flags); - free_irq(DMA9_INTR_VECT, NULL); + free_irq(DMA_IRQ, NULL); - (void)crisv32_free_dma(8); - (void)crisv32_free_dma(9); + (void)crisv32_free_dma(OUT_DMA); + (void)crisv32_free_dma(IN_DMA); } @@ -2076,13 +2079,13 @@ static void cryptocop_job_queue_close(void) reg_dma_rw_cfg dma_out_cfg, dma_in_cfg; /* Stop DMA. */ - dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg); + dma_out_cfg = REG_RD(dma, OUT_DMA_INST, rw_cfg); dma_out_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg); + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_out_cfg); - dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg); + dma_in_cfg = REG_RD(dma, IN_DMA_INST, rw_cfg); dma_in_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg); + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_in_cfg); /* Disble the cryptocop. */ rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg); @@ -2226,10 +2229,11 @@ static void cryptocop_start_job(void) &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out))); /* Start input DMA. */ - DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in)); + flush_dma_context(&pj->iop->ctx_in); + DMA_START_CONTEXT(IN_DMA_INST, virt_to_phys(&pj->iop->ctx_in)); /* Start output DMA. */ - DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out)); + DMA_START_CONTEXT(OUT_DMA_INST, virt_to_phys(&pj->iop->ctx_out)); spin_unlock_irqrestore(&running_job_lock, running_job_flags); DEBUG(printk("cryptocop_start_job: exiting\n")); diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c index f1edd2e359b..c2fb7a5c139 100644 --- a/arch/cris/arch-v32/drivers/i2c.c +++ b/arch/cris/arch-v32/drivers/i2c.c @@ -19,10 +19,10 @@ *! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */ + /****************** INCLUDE FILES SECTION ***********************************/ #include <linux/module.h> @@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ + /****************** VARIABLE SECTION ************************************/ static struct crisv32_iopin cris_i2c_clk; @@ -252,6 +254,7 @@ i2c_getack(void) * generate ACK clock pulse */ i2c_clk(I2C_CLOCK_HIGH); +#if 0 /* * Use PORT PB instead of I2C * for input. (I2C not working) @@ -264,6 +267,8 @@ i2c_getack(void) i2c_data(1); i2c_disable(); i2c_dir_in(); +#endif + /* * now wait for ack */ @@ -271,11 +276,11 @@ i2c_getack(void) /* * check for ack */ - if(i2c_getbit()) + if (i2c_getbit()) ack = 0; i2c_delay(CLOCK_HIGH_TIME/2); - if(!ack){ - if(!i2c_getbit()) /* receiver pulled SDA low */ + if (!ack) { + if (!i2c_getbit()) /* receiver pulld SDA low */ ack = 1; i2c_delay(CLOCK_HIGH_TIME/2); } @@ -285,6 +290,7 @@ i2c_getack(void) * before we enable our output. If we keep data high * and enable output, we would generate a stop condition. */ +#if 0 i2c_data(I2C_DATA_LOW); /* @@ -292,6 +298,7 @@ i2c_getack(void) */ i2c_enable(); i2c_dir_out(); +#endif i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_HIGH_TIME/4); /* @@ -375,6 +382,121 @@ i2c_sendnack(void) /*#--------------------------------------------------------------------------- *# +*# FUNCTION NAME: i2c_write +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_write(unsigned char theSlave, void *data, size_t nbytes) +{ + int error, cntr = 3; + unsigned char bytes_wrote = 0; + unsigned char value; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * send data + */ + for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) { + memcpy(&value, data + bytes_wrote, sizeof value); + i2c_outbyte(value); + /* + * now it's time to wait for ack + */ + if (!i2c_getack()) + error |= 4; + } + /* + * end byte stream + */ + i2c_stop(); + + } while (error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_read +*# +*# DESCRIPTION : Reads a value from an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_read(unsigned char theSlave, void *data, size_t nbytes) +{ + unsigned char b = 0; + unsigned char bytes_read = 0; + int error, cntr = 3; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + memset(data, 0, nbytes); + /* + * generate start condition + */ + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave | 0x01)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * fetch data + */ + for (bytes_read = 0; bytes_read < nbytes; bytes_read++) { + b = i2c_inbyte(); + memcpy(data + bytes_read, &b, sizeof b); + + if (bytes_read < (nbytes - 1)) + i2c_sendack(); + } + /* + * last received byte needs to be nacked + * instead of acked + */ + i2c_sendnack(); + /* + * end sequence + */ + i2c_stop(); + } while (error && cntr--); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# *# FUNCTION NAME: i2c_writereg *# *# DESCRIPTION : Writes a value to an I2C device @@ -387,12 +509,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); i2c_start(); /* @@ -427,15 +547,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, * end byte stream */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); - } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); + spin_unlock_irqrestore(&i2c_lock, flags); + return -error; } @@ -453,13 +570,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; /* - * we don't like to be interrupted - */ - local_irq_save(flags); - /* * generate start condition */ i2c_start(); @@ -482,7 +597,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * now it's time to wait for ack */ if(!i2c_getack()) - error = 1; + error |= 2; /* * repeat start condition */ @@ -496,7 +611,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * wait for ack */ if(!i2c_getack()) - error = 1; + error |= 4; /* * fetch register */ @@ -510,13 +625,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * end sequence */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while(error && cntr--); + spin_unlock_irqrestore(&i2c_lock, flags); + return b; } @@ -540,7 +653,7 @@ i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { - return -EINVAL; + return -ENOTTY; } switch (_IOC_NR(cmd)) { @@ -580,31 +693,52 @@ static const struct file_operations i2c_fops = { .release = i2c_release, }; -int __init -i2c_init(void) +static int __init i2c_init(void) { - int res; + static int res; + static int first = 1; - /* Setup and enable the Port B I2C interface */ + if (!first) + return res; - crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); - crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + first = 0; + + /* Setup and enable the DATA and CLK pins */ + + res = crisv32_io_get_name(&cris_i2c_data, + CONFIG_ETRAX_V32_I2C_DATA_PORT); + if (res < 0) + return res; + + res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT); + crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out); + + return res; +} + + +static int __init i2c_register(void) +{ + int res; + + res = i2c_init(); + if (res < 0) + return res; /* register char device */ res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO + "I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n"); return 0; } - /* this makes sure that i2c_init is called during boot */ - -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h index bfe1a13f9f3..c073cf4ba01 100644 --- a/arch/cris/arch-v32/drivers/i2c.h +++ b/arch/cris/arch-v32/drivers/i2c.h @@ -3,6 +3,8 @@ /* High level I2C actions */ int __init i2c_init(void); +int i2c_write(unsigned char theSlave, void *data, size_t nbytes); +int i2c_read(unsigned char theSlave, void *data, size_t nbytes); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); diff --git a/arch/cris/arch-v32/drivers/iop_fw_load.c b/arch/cris/arch-v32/drivers/iop_fw_load.c index f4bdc1dfa32..3b3857ec1f1 100644 --- a/arch/cris/arch-v32/drivers/iop_fw_load.c +++ b/arch/cris/arch-v32/drivers/iop_fw_load.c @@ -1,5 +1,4 @@ -/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $ - * +/* * Firmware loader for ETRAX FS IO-Processor * * Copyright (C) 2004 Axis Communications AB @@ -11,12 +10,13 @@ #include <linux/device.h> #include <linux/firmware.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/iop/iop_reg_space.h> -#include <asm/arch/hwregs/iop/iop_mpu_macros.h> -#include <asm/arch/hwregs/iop/iop_mpu_defs.h> -#include <asm/arch/hwregs/iop/iop_spu_defs.h> -#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/iop/iop_reg_space.h> +#include <hwregs/iop/iop_mpu_macros.h> +#include <hwregs/iop/iop_mpu_defs.h> +#include <hwregs/iop/iop_spu_defs.h> +#include <hwregs/iop/iop_sw_cpu_defs.h> #define IOP_TIMEOUT 100 diff --git a/arch/cris/arch-v32/drivers/mach-a3/Makefile b/arch/cris/arch-v32/drivers/mach-a3/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/mach-a3/gpio.c b/arch/cris/arch-v32/drivers/mach-a3/gpio.c new file mode 100644 index 00000000000..de107dad9f4 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/gpio.c @@ -0,0 +1,984 @@ +/* + * Artec-3 general port I/O device + * + * Copyright (c) 2007 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G, + * port to ETRAX FS. + * Ricard Wanderlof (PWM for Artpec-3) + * + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include <asm/etraxgpio.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/arch/mach/pinmux.h> + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + +/* The following gio ports on ARTPEC-3 is available: + * pa 32 bits + * pb 32 bits + * pc 16 bits + * each port has a rw_px_dout, r_px_din and rw_px_oe register. + */ + +#define GPIO_MAJOR 120 /* experimental MAJOR number */ + +#define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */ + +#define D(x) + +#if 0 +static int dp_cnt; +#define DP(x) \ + do { \ + dp_cnt++; \ + if (dp_cnt % 1000 == 0) \ + x; \ + } while (0) +#else +#define DP(x) +#endif + +static char gpio_name[] = "etrax gpio"; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off); +static int gpio_open(struct inode *inode, struct file *filp); +static int gpio_release(struct inode *inode, struct file *filp); +static unsigned int gpio_poll(struct file *filp, + struct poll_table_struct *wait); + +/* private data per open() of this driver */ + +struct gpio_private { + struct gpio_private *next; + /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */ + unsigned char clk_mask; + unsigned char data_mask; + unsigned char write_msb; + unsigned char pad1; + /* These fields are generic */ + unsigned long highalarm, lowalarm; + wait_queue_head_t alarm_wq; + int minor; +}; + +static void gpio_set_alarm(struct gpio_private *priv); +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg); +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg); + + +/* linked list of alarms to check for */ + +static struct gpio_private *alarmlist; + +static int wanted_interrupts; + +static DEFINE_SPINLOCK(gpio_lock); + +#define NUM_PORTS (GPIO_MINOR_LAST+1) +#define GIO_REG_RD_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg) +static unsigned long led_dummy; +static unsigned long port_d_dummy; /* Only input on Artpec-3 */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long port_e_dummy; /* Non existent on Artpec-3 */ +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif + +static unsigned long *data_out[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_dout), + GIO_REG_WR_ADDR(rw_pb_dout), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_dout), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static unsigned long *data_in[NUM_PORTS] = { + GIO_REG_RD_ADDR(r_pa_din), + GIO_REG_RD_ADDR(r_pb_din), + &led_dummy, + GIO_REG_RD_ADDR(r_pc_din), + GIO_REG_RD_ADDR(r_pd_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static unsigned long changeable_dir[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_DIR, + CONFIG_ETRAX_PB_CHANGEABLE_DIR, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_DIR, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif +}; + +static unsigned long changeable_bits[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_BITS, + CONFIG_ETRAX_PB_CHANGEABLE_BITS, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_BITS, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif +}; + +static unsigned long *dir_oe[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_oe), + GIO_REG_WR_ADDR(rw_pb_oe), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_oe), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_rw_pv_oe, +#endif +}; + +static void gpio_set_alarm(struct gpio_private *priv) +{ + int bit; + int intr_cfg; + int mask; + int pins; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + intr_cfg = REG_RD_INT(gio, regi_gio, rw_intr_cfg); + pins = REG_RD_INT(gio, regi_gio, rw_intr_pins); + mask = REG_RD_INT(gio, regi_gio, rw_intr_mask) & I2C_INTERRUPT_BITS; + + for (bit = 0; bit < 32; bit++) { + int intr = bit % 8; + int pin = bit / 8; + if (priv->minor < GPIO_MINOR_LEDS) + pin += priv->minor * 4; + else + pin += (priv->minor - 1) * 4; + + if (priv->highalarm & (1<<bit)) { + intr_cfg |= (regk_gio_hi << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } else if (priv->lowalarm & (1<<bit)) { + intr_cfg |= (regk_gio_lo << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } + } + + REG_WR_INT(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR_INT(gio, regi_gio, rw_intr_pins, pins); + REG_WR_INT(gio, regi_gio, rw_intr_mask, mask); + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct gpio_private *priv = file->private_data; + unsigned long data; + unsigned long tmp; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return 0; + + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor <= GPIO_MINOR_D) { + data = readl(data_in[priv->minor]); + REG_WR_INT(gio, regi_gio, rw_ack_intr, wanted_interrupts); + tmp = REG_RD_INT(gio, regi_gio, rw_intr_mask); + tmp &= I2C_INTERRUPT_BITS; + tmp |= wanted_interrupts; + REG_WR_INT(gio, regi_gio, rw_intr_mask, tmp); + } else + return 0; + + if ((data & priv->highalarm) || (~data & priv->lowalarm)) + mask = POLLIN|POLLRDNORM; + + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +static irqreturn_t gpio_interrupt(int irq, void *dev_id) +{ + reg_gio_rw_intr_mask intr_mask; + reg_gio_r_masked_intr masked_intr; + reg_gio_rw_ack_intr ack_intr; + unsigned long flags; + unsigned long tmp; + unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif + + /* Find what PA interrupts are active */ + masked_intr = REG_RD(gio, regi_gio, r_masked_intr); + tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); + + /* Find those that we have enabled */ + spin_lock_irqsave(&gpio_lock, flags); + tmp &= wanted_interrupts; + spin_unlock_irqrestore(&gpio_lock, flags); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + + /* Ack them */ + ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); + REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); + + /* Disable those interrupts.. */ + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); + tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + return IRQ_RETVAL(tmp); +} + +static void gpio_write_bit(unsigned long *port, unsigned char data, int bit, + unsigned char clk_mask, unsigned char data_mask) +{ + unsigned long shadow = readl(port) & ~clk_mask; + writel(shadow, port); + if (data & 1 << bit) + shadow |= data_mask; + else + shadow &= ~data_mask; + writel(shadow, port); + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + shadow |= clk_mask; + writel(shadow, port); +} + +static void gpio_write_byte(struct gpio_private *priv, unsigned long *port, + unsigned char data) +{ + int i; + + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); +} + + +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; + ssize_t retval = count; + /* Only bits 0-7 may be used for write operations but allow all + devices except leds... */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) + return -EFAULT; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (priv->clk_mask == 0 || priv->data_mask == 0) + return -EPERM; + + D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " + "msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + spin_lock_irqsave(&gpio_lock, flags); + + while (count--) + gpio_write_byte(priv, data_out[priv->minor], *buf++); + + spin_unlock_irqrestore(&gpio_lock, flags); + return retval; +} + +static int gpio_open(struct inode *inode, struct file *filp) +{ + struct gpio_private *priv; + int p = iminor(inode); + + if (p > GPIO_MINOR_LAST_PWM || + (p > GPIO_MINOR_LAST && p < GPIO_MINOR_PWM0)) + return -EINVAL; + + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + priv->minor = p; + filp->private_data = priv; + + /* initialize the io/alarm struct, not for PWM ports though */ + if (p <= GPIO_MINOR_LAST) { + + priv->clk_mask = 0; + priv->data_mask = 0; + priv->highalarm = 0; + priv->lowalarm = 0; + + init_waitqueue_head(&priv->alarm_wq); + + /* link it into our alarmlist */ + spin_lock_irq(&gpio_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&gpio_lock); + } + + return 0; +} + +static int gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p; + struct gpio_private *todel; + /* local copies while updating them: */ + unsigned long a_high, a_low; + + /* prepare to free private structure */ + todel = filp->private_data; + + /* unlink from alarmlist - only for non-PWM ports though */ + if (todel->minor <= GPIO_MINOR_LAST) { + spin_lock_irq(&gpio_lock); + p = alarmlist; + + if (p == todel) + alarmlist = todel->next; + else { + while (p->next != todel) + p = p->next; + p->next = todel->next; + } + + /* Check if there are still any alarms set */ + p = alarmlist; + a_high = 0; + a_low = 0; + while (p) { + if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + a_high |= p->highalarm; + a_low |= p->lowalarm; + } + + p = p->next; + } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variable 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + + spin_unlock_irq(&gpio_lock); + } + kfree(todel); + + return 0; +} + +/* Main device API. ioctl's to read/set/clear bits, as well as to + * set alarms to wait for using a subsequent select(). + */ + +inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) +{ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + unsigned long flags; + unsigned long dir_shadow; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) & + ~(arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + + if (priv->minor == GPIO_MINOR_C) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif + else + dir_shadow ^= 0xFFFFFFFF; /* PA, PB and PD 32 bits */ + + return dir_shadow; + +} /* setget_input */ + +static inline unsigned long setget_output(struct gpio_private *priv, + unsigned long arg) +{ + unsigned long flags; + unsigned long dir_shadow; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) | + (arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + return dir_shadow; +} /* setget_output */ + +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned long val; + unsigned long shadow; + struct gpio_private *priv = file->private_data; + + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) + return -ENOTTY; + + /* Check for special ioctl handlers first */ + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif + + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return gpio_pwm_ioctl(priv, cmd, arg); + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + /* Read the port. */ + return readl(data_in[priv->minor]); + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) | + (arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + gpio_set_alarm(priv); + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + return readl(dir_oe[priv->minor]); + + case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + return setget_input(priv, arg); + + case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ + /* Set direction 0=unchanged 1=output, + * return mask with 1=output + */ + return setget_output(priv, arg); + + case IO_CFG_WRITE_MODE: + { + int res = -EPERM; + unsigned long dir_shadow, clk_mask, data_mask, write_msb; + + clk_mask = arg & 0xFF; + data_mask = (arg >> 8) & 0xFF; + write_msb = (arg >> 16) & 0x01; + + /* Check if we're allowed to change the bits and + * the direction is correct + */ + spin_lock_irqsave(&gpio_lock, flags); + dir_shadow = readl(dir_oe[priv->minor]); + if ((clk_mask & changeable_bits[priv->minor]) && + (data_mask & changeable_bits[priv->minor]) && + (clk_mask & dir_shadow) && + (data_mask & dir_shadow)) { + priv->clk_mask = clk_mask; + priv->data_mask = data_mask; + priv->write_msb = write_msb; + res = 0; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + return res; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = readl(data_in[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + val = *data_out[priv->minor]; + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) | + (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = readl(dir_oe[priv->minor]); + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read & ~readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~readl(dir_oe[priv->minor]); + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + unsigned char green; + unsigned char red; + + switch (_IOC_NR(cmd)) { + case IO_LEDACTIVE_SET: + green = ((unsigned char) arg) & 1; + red = (((unsigned char) arg) >> 1) & 1; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +static int gpio_pwm_set_mode(unsigned long arg, int pwm_port) +{ + int pinmux_pwm = pinmux_pwm0 + pwm_port; + int mode; + reg_gio_rw_pwm0_ctrl rw_pwm_ctrl = { + .ccd_val = 0, + .ccd_override = regk_gio_no, + .mode = regk_gio_no + }; + int allocstatus; + + if (get_user(mode, &((struct io_pwm_set_mode *) arg)->mode)) + return -EFAULT; + rw_pwm_ctrl.mode = mode; + if (mode != PWM_OFF) + allocstatus = crisv32_pinmux_alloc_fixed(pinmux_pwm); + else + allocstatus = crisv32_pinmux_dealloc_fixed(pinmux_pwm); + if (allocstatus) + return allocstatus; + REG_WRITE(reg_gio_rw_pwm0_ctrl, REG_ADDR(gio, regi_gio, rw_pwm0_ctrl) + + 12 * pwm_port, rw_pwm_ctrl); + return 0; +} + +static int gpio_pwm_set_period(unsigned long arg, int pwm_port) +{ + struct io_pwm_set_period periods; + reg_gio_rw_pwm0_var rw_pwm_widths; + + if (copy_from_user(&periods, (void __user *)arg, sizeof(periods))) + return -EFAULT; + if (periods.lo > 8191 || periods.hi > 8191) + return -EINVAL; + rw_pwm_widths.lo = periods.lo; + rw_pwm_widths.hi = periods.hi; + REG_WRITE(reg_gio_rw_pwm0_var, REG_ADDR(gio, regi_gio, rw_pwm0_var) + + 12 * pwm_port, rw_pwm_widths); + return 0; +} + +static int gpio_pwm_set_duty(unsigned long arg, int pwm_port) +{ + unsigned int duty; + reg_gio_rw_pwm0_data rw_pwm_duty; + + if (get_user(duty, &((struct io_pwm_set_duty *) arg)->duty)) + return -EFAULT; + if (duty > 255) + return -EINVAL; + rw_pwm_duty.data = duty; + REG_WRITE(reg_gio_rw_pwm0_data, REG_ADDR(gio, regi_gio, rw_pwm0_data) + + 12 * pwm_port, rw_pwm_duty); + return 0; +} + +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg) +{ + int pwm_port = priv->minor - GPIO_MINOR_PWM0; + + switch (_IOC_NR(cmd)) { + case IO_PWM_SET_MODE: + return gpio_pwm_set_mode(arg, pwm_port); + case IO_PWM_SET_PERIOD: + return gpio_pwm_set_period(arg, pwm_port); + case IO_PWM_SET_DUTY: + return gpio_pwm_set_duty(arg, pwm_port); + default: + return -EINVAL; + } + return 0; +} + +static const struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void __init virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); +} +#endif + +/* main driver initialization routine, called from mem.c */ + +static int __init gpio_init(void) +{ + int res; + + printk(KERN_INFO "ETRAX FS GPIO driver v2.7, (c) 2003-2008 " + "Axis Communications AB\n"); + + /* do the formalities */ + + res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); + if (res < 0) { + printk(KERN_ERR "gpio: couldn't get a major number.\n"); + return res; + } + + /* Clear all leds */ + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + int res2 = request_irq(GIO_INTR_VECT, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio", &alarmlist); + if (res2) { + printk(KERN_ERR "err: irq for gpio\n"); + return res2; + } + + /* No IRQs by default. */ + REG_WR_INT(gio, regi_gio, rw_intr_pins, 0); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif + + return res; +} + +/* this makes sure that gpio_init is called during kernel boot */ + +module_init(gpio_init); diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c new file mode 100644 index 00000000000..01ed0be2d0d --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c @@ -0,0 +1,180 @@ +/* + * arch/cris/arch-v32/drivers/nandflash.c + * + * Copyright (c) 2007 + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * 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. + * + */ + +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/arch/memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/pio_defs.h> +#include <pinmux.h> +#include <asm/io.h> + +#define MANUAL_ALE_CLE_CONTROL 1 + +#define regf_ALE a0 +#define regf_CLE a1 +#define regf_NCE ce0_n + +#define CLE_BIT 10 +#define ALE_BIT 11 +#define CE_BIT 12 + +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +static struct mtd_info *crisv32_mtd; +/* + * hardware specific access to control-lines + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + unsigned long flags; + reg_pio_rw_dout dout; + struct nand_chip *this = mtd->priv; + + local_irq_save(flags); + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(pio, regi_pio, rw_dout); + dout.regf_NCE = (ctrl & NAND_NCE) ? 0 : 1; + +#if !MANUAL_ALE_CLE_CONTROL + if (ctrl & NAND_ALE) { + /* A0 = ALE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access1); + } else if (ctrl & NAND_CLE) { + /* A1 = CLE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access2); + } else { + /* A1 = CLE and A0 = ALE low */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access0); + } +#else + + dout.regf_CLE = (ctrl & NAND_CLE) ? 1 : 0; + dout.regf_ALE = (ctrl & NAND_ALE) ? 1 : 0; +#endif + REG_WR(pio, regi_pio, rw_dout, dout); + } + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + + local_irq_restore(flags); +} + +/* +* read device ready pin +*/ +static int crisv32_device_ready(struct mtd_info *mtd) +{ + reg_pio_r_din din = REG_RD(pio, regi_pio, r_din); + return din.rdy; +} + +/* + * Main initialization routine + */ +struct mtd_info *__init crisv32_nand_flash_probe(void) +{ + void __iomem *read_cs; + void __iomem *write_cs; + + struct mtd_info_wrapper *wrapper; + struct nand_chip *this; + int err = 0; + + reg_pio_rw_man_ctrl man_ctrl = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_oe oe = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_dout dout = { .regf_NCE = 1 }; + + /* Allocate pio pins to pio */ + crisv32_pinmux_alloc_fixed(pinmux_pio); + /* Set up CE, ALE, CLE (ce0_n, a0, a1) for manual control and output */ + REG_WR(pio, regi_pio, rw_man_ctrl, man_ctrl); + REG_WR(pio, regi_pio, rw_dout, dout); + REG_WR(pio, regi_pio, rw_oe, oe); + + /* Allocate memory for MTD device structure and private data */ + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); + err = -ENOMEM; + return NULL; + } + + read_cs = write_cs = (void __iomem *)REG_ADDR(pio, regi_pio, + rw_io_access0); + + /* Get pointer to private data */ + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; + + /* Link the private data with the MTD structure */ + crisv32_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = read_cs; + this->IO_ADDR_W = write_cs; + this->cmd_ctrl = crisv32_hwcontrol; + this->dev_ready = crisv32_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + + /* Enable the following for a flash based bad block table */ + /* this->options = NAND_USE_FLASH_BBT; */ + + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { + err = -ENXIO; + goto out_mtd; + } + + return crisv32_mtd; + +out_mtd: + kfree(wrapper); + return NULL; +} + diff --git a/arch/cris/arch-v32/drivers/mach-fs/Makefile b/arch/cris/arch-v32/drivers/mach-fs/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-fs/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/gpio.c b/arch/cris/arch-v32/drivers/mach-fs/gpio.c index d82c5c56113..7863fd4efc2 100644 --- a/arch/cris/arch-v32/drivers/gpio.c +++ b/arch/cris/arch-v32/drivers/mach-fs/gpio.c @@ -1,68 +1,15 @@ -/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ - * +/* * ETRAX CRISv32 general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 1999-2006 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G, * port to ETRAX FS. * - * $Log: gpio.c,v $ - * Revision 1.16 2005/06/19 17:06:49 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.15 2005/05/25 08:22:20 starvik - * Changed GPIO port order to fit packages/devices/axis-2.4. - * - * Revision 1.14 2005/04/24 18:35:08 starvik - * Updated with final register headers. - * - * Revision 1.13 2005/03/15 15:43:00 starvik - * dev_id needs to be supplied for shared IRQs. - * - * Revision 1.12 2005/03/10 17:12:00 starvik - * Protect alarm list with spinlock. - * - * Revision 1.11 2005/01/05 06:08:59 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.10 2004/11/19 08:38:31 starvik - * Removed old crap. - * - * Revision 1.9 2004/05/14 07:58:02 starvik - * Merge of changes from 2.4 - * - * Revision 1.8 2003/09/11 07:29:50 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.7 2003/07/10 13:25:46 starvik - * Compiles for 2.5.74 - * Lindented ethernet.c - * - * Revision 1.6 2003/07/04 08:27:46 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/10 08:26:37 johana - * Etrax -> ETRAX CRISv32 - * - * Revision 1.4 2003/06/05 14:22:48 johana - * Initialise some_alarms. - * - * Revision 1.3 2003/06/05 10:15:46 johana - * New INTR_VECT macros. - * Enable interrupts in global config. - * - * Revision 1.2 2003/06/03 15:52:50 johana - * Initial CRIS v32 version. - * - * Revision 1.1 2003/06/03 08:53:15 johana - * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. - * */ - #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> @@ -77,14 +24,20 @@ #include <linux/spinlock.h> #include <asm/etraxgpio.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/gio_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> #include <asm/io.h> #include <asm/system.h> #include <asm/irq.h> +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + /* The following gio ports on ETRAX FS is available: * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge * pb 18 bits @@ -100,7 +53,12 @@ #if 0 static int dp_cnt; -#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) +#define DP(x) \ + do { \ + dp_cnt++; \ + if (dp_cnt % 1000 == 0) \ + x; \ + } while (0) #else #define DP(x) #endif @@ -111,13 +69,18 @@ static char gpio_name[] = "etrax gpio"; static wait_queue_head_t *gpio_wq; #endif +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char *buf, size_t count, + loff_t *off); static int gpio_open(struct inode *inode, struct file *filp); static int gpio_release(struct inode *inode, struct file *filp); -static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); +static unsigned int gpio_poll(struct file *filp, + struct poll_table_struct *wait); /* private data per open() of this driver */ @@ -136,18 +99,25 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -static int gpio_some_alarms = 0; /* Set if someone uses alarm */ -static unsigned long gpio_pa_high_alarms = 0; -static unsigned long gpio_pa_low_alarms = 0; +static int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_high_alarms; +static unsigned long gpio_pa_low_alarms; static DEFINE_SPINLOCK(alarm_lock); #define NUM_PORTS (GPIO_MINOR_LAST+1) -#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) -#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) +#define GIO_REG_RD_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) unsigned long led_dummy; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pa_dout), @@ -156,6 +126,9 @@ static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_dout), GIO_REG_WR_ADDR(rw_pd_dout), GIO_REG_WR_ADDR(rw_pe_dout), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static volatile unsigned long *data_in[NUM_PORTS] = { @@ -165,6 +138,9 @@ static volatile unsigned long *data_in[NUM_PORTS] = { GIO_REG_RD_ADDR(r_pc_din), GIO_REG_RD_ADDR(r_pd_din), GIO_REG_RD_ADDR(r_pe_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static unsigned long changeable_dir[NUM_PORTS] = { @@ -174,6 +150,9 @@ static unsigned long changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_DIR, CONFIG_ETRAX_PD_CHANGEABLE_DIR, CONFIG_ETRAX_PE_CHANGEABLE_DIR, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif }; static unsigned long changeable_bits[NUM_PORTS] = { @@ -183,6 +162,9 @@ static unsigned long changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_BITS, CONFIG_ETRAX_PD_CHANGEABLE_BITS, CONFIG_ETRAX_PE_CHANGEABLE_BITS, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif }; static volatile unsigned long *dir_oe[NUM_PORTS] = { @@ -192,13 +174,14 @@ static volatile unsigned long *dir_oe[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_oe), GIO_REG_WR_ADDR(rw_pd_oe), GIO_REG_WR_ADDR(rw_pe_oe), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_rw_pv_oe, +#endif }; -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; @@ -210,65 +193,50 @@ gpio_poll(struct file *file, unsigned long flags; local_irq_save(flags); - data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din)); + data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, + REG_RD(gio, regi_gio, r_pa_din)); /* PA has support for interrupt * lets activate high for those low and with highalarm set */ intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); tmp = ~data & priv->highalarm & 0xFF; - if (tmp & (1 << 0)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_hi; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_hi; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_hi; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_hi; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_hi; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_hi; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_hi; - } - if (tmp & (1 << 7)) { + if (tmp & (1 << 7)) intr_cfg.pa7 = regk_gio_hi; - } /* * lets activate low for those high and with lowalarm set */ tmp = data & priv->lowalarm & 0xFF; - if (tmp & (1 << 0)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_lo; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_lo; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_lo; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_lo; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_lo; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_lo; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_lo; - } - if (tmp & (1 << 7)) { + if (tmp & (1 << 7)) intr_cfg.pa7 = regk_gio_lo; - } REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); local_irq_restore(flags); @@ -277,50 +245,65 @@ gpio_poll(struct file *file, else return 0; - if ((data & priv->highalarm) || - (~data & priv->lowalarm)) { + if ((data & priv->highalarm) || (~data & priv->lowalarm)) mask = POLLIN|POLLRDNORM; - } - DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); return mask; } int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; - int ret = 0; + unsigned long flags; + int ret = 0; + spin_lock_irqsave(&alarm_lock, flags); + priv = alarmlist; while (priv) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + data = (unsigned long)cached_virtual_gpio_read; + else { + data = *data_in[priv->minor]; + if (priv->minor == GPIO_MINOR_A) + priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + } +#else data = *data_in[priv->minor]; +#endif if ((data & priv->highalarm) || (~data & priv->lowalarm)) { - DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); + DP(printk(KERN_DEBUG + "etrax_gpio_wake_up_check %i\n", priv->minor)); wake_up_interruptible(&priv->alarm_wq); - ret = 1; + ret = 1; } priv = priv->next; } - return ret; + spin_unlock_irqrestore(&alarm_lock, flags); + return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_pa_interrupt(int irq, void *dev_id) { reg_gio_rw_intr_mask intr_mask; reg_gio_r_masked_intr masked_intr; reg_gio_rw_ack_intr ack_intr; unsigned long tmp; unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif /* Find what PA interrupts are active */ masked_intr = REG_RD(gio, regi_gio, r_masked_intr); @@ -331,6 +314,17 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); spin_unlock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + /* Ack them */ ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); @@ -339,18 +333,24 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +static ssize_t gpio_write(struct file *file, const char *buf, size_t count, + loff_t *off) { struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned char data, clk_mask, data_mask, write_msb; @@ -360,29 +360,31 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, ssize_t retval = count; /* Only bits 0-7 may be used for write operations but allow all devices except leds... */ - if (priv->minor == GPIO_MINOR_LEDS) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) return -EFAULT; - } - if (!access_ok(VERIFY_READ, buf, count)) { + if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; - } clk_mask = priv->clk_mask; data_mask = priv->data_mask; /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { + if (clk_mask == 0 || data_mask == 0) return -EPERM; - } write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); + D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " + "msb: %i\n", count, data_mask, clk_mask, write_msb)); port = data_out[priv->minor]; while (count--) { int i; data = *buf++; if (priv->write_msb) { - for (i = 7; i >= 0;i--) { + for (i = 7; i >= 0; i--) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -395,7 +397,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, local_irq_restore(flags); } } else { - for (i = 0; i <= 7;i++) { + for (i = 0; i <= 7; i++) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -423,18 +425,16 @@ gpio_open(struct inode *inode, struct file *filp) if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; + memset(priv, 0, sizeof(*priv)); priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; priv->clk_mask = 0; priv->data_mask = 0; priv->highalarm = 0; @@ -443,20 +443,30 @@ gpio_open(struct inode *inode, struct file *filp) filp->private_data = (void *)priv; + /* link it into our alarmlist */ + spin_lock_irq(&alarm_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&alarm_lock); + return 0; } static int gpio_release(struct inode *inode, struct file *filp) { - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; + struct gpio_private *p; + struct gpio_private *todel; /* local copies while updating them: */ unsigned long a_high, a_low; unsigned long some_alarms; /* unlink from alarmlist and free the private structure */ + spin_lock_irq(&alarm_lock); + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; + if (p == todel) { alarmlist = todel->next; } else { @@ -468,26 +478,35 @@ gpio_release(struct inode *inode, struct file *filp) kfree(todel); /* Check if there are still any alarms set */ p = alarmlist; - some_alarms = 0; + some_alarms = 0; a_high = 0; a_low = 0; while (p) { if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif a_high |= p->highalarm; a_low |= p->lowalarm; } - if (p->highalarm | p->lowalarm) { + if (p->highalarm | p->lowalarm) some_alarms = 1; - } p = p->next; } - spin_lock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variables 'some_alarms' and 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + some_alarms = 1; + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + gpio_some_alarms = some_alarms; gpio_pa_high_alarms = a_high; gpio_pa_low_alarms = a_low; - spin_unlock(&alarm_lock); + spin_unlock_irq(&alarm_lock); return 0; } @@ -496,7 +515,7 @@ gpio_release(struct inode *inode, struct file *filp) * set alarms to wait for using a subsequent select(). */ -unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) { /* Set direction 0=unchanged 1=input, * return mask with 1=input @@ -512,13 +531,17 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) if (priv->minor == GPIO_MINOR_A) dir_shadow ^= 0xFF; /* Only 8 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif else dir_shadow ^= 0x3FFFF; /* Only 18 bits */ return dir_shadow; } /* setget_input */ -unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg) { unsigned long flags; unsigned long dir_shadow; @@ -542,20 +565,22 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; unsigned long shadow; struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ - // read the port + /* Read the port. */ return *data_in[priv->minor]; break; case IO_SETBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO SET 2\n"); - // set changeable bits with a 1 in arg + /* Set changeable bits with a 1 in arg. */ shadow = *data_out[priv->minor]; shadow |= (arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; @@ -563,46 +588,42 @@ gpio_ioctl(struct inode *inode, struct file *file, break; case IO_CLRBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO CLR 2\n"); - // clear changeable bits with a 1 in arg + /* Clear changeable bits with a 1 in arg. */ shadow = *data_out[priv->minor]; shadow &= ~(arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; local_irq_restore(flags); break; case IO_HIGHALARM: - // set alarm when bits with 1 in arg go high + /* Set alarm when bits with 1 in arg go high. */ priv->highalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_high_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_LOWALARM: - // set alarm when bits with 1 in arg go low + /* Set alarm when bits with 1 in arg go low. */ priv->lowalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_low_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_CLRALARM: - // clear alarm for bits with 1 in arg + /* Clear alarm for bits with 1 in arg. */ priv->highalarm &= ~arg; priv->lowalarm &= ~arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); if (priv->minor == GPIO_MINOR_A) { if (gpio_pa_high_alarms & arg || - gpio_pa_low_alarms & arg) { + gpio_pa_low_alarms & arg) /* Must update the gpio_pa_*alarms masks */ - } + ; } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ @@ -633,8 +654,7 @@ gpio_ioctl(struct inode *inode, struct file *file, if (!((priv->clk_mask & changeable_bits[priv->minor]) && (priv->data_mask & changeable_bits[priv->minor]) && (priv->clk_mask & dir_shadow) && - (priv->data_mask & dir_shadow))) - { + (priv->data_mask & dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; return -EPERM; @@ -644,34 +664,34 @@ gpio_ioctl(struct inode *inode, struct file *file, case IO_READ_INBITS: /* *arg is result of reading the input pins */ val = *data_in[priv->minor]; - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; return 0; break; case IO_READ_OUTBITS: /* *arg is result of reading the output shadow */ val = *data_out[priv->minor]; - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) return -EFAULT; val = setget_input(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) return -EFAULT; val = setget_output(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; default: @@ -684,6 +704,133 @@ gpio_ioctl(struct inode *inode, struct file *file, return 0; } +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int +virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + local_irq_save(flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow |= (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow &= ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + spin_lock(&alarm_lock); + spin_unlock(&alarm_lock); + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = *dir_oe[priv->minor]; + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read; + val &= ~*dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= *dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~*dir_oe[priv->minor]; + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) { @@ -694,8 +841,8 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) case IO_LEDACTIVE_SET: green = ((unsigned char) arg) & 1; red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; default: @@ -705,7 +852,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -714,6 +861,66 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void +virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + gpio_some_alarms = 1; +} +#endif /* main driver initialization routine, called from mem.c */ @@ -721,7 +928,6 @@ static __init int gpio_init(void) { int res; - reg_intr_vect_rw_mask intr_mask; /* do the formalities */ @@ -732,30 +938,30 @@ gpio_init(void) } /* Clear all leds */ - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); - - printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n"); + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + printk(KERN_INFO "ETRAX FS GPIO driver v2.5, (c) 2003-2007 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. */ - if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) { - printk("err: timer0 irq for gpio\n"); - } - if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", &alarmlist)) { - printk("err: PA irq for gpio\n"); - } - /* enable the gio and timer irq in global config */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.timer = 1; - intr_mask.gen_io = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + if (request_irq(TIMER0_INTR_VECT, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", &alarmlist)) + printk(KERN_ERR "timer0 irq for gpio\n"); + + if (request_irq(GIO_INTR_VECT, gpio_pa_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", &alarmlist)) + printk(KERN_ERR "PA irq for gpio\n"); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif return res; } diff --git a/arch/cris/arch-v32/drivers/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c index 5ce015c6bb0..aa01b134458 100644 --- a/arch/cris/arch-v32/drivers/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c @@ -4,9 +4,7 @@ * Copyright (c) 2004 * * Derived from drivers/mtd/nand/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $ + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * 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 @@ -21,10 +19,10 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <asm/arch/memmap.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/gio_defs.h> -#include <asm/arch/hwregs/bif_core_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/bif_core_defs.h> #include <asm/io.h> #define CE_BIT 4 @@ -32,44 +30,65 @@ #define ALE_BIT 6 #define BY_BIT 7 -static struct mtd_info *crisv32_mtd = NULL; +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +/* Bitmask for mtd nand control bits */ +#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE) + + +static struct mtd_info *crisv32_mtd; /* * hardware specific access to control-lines -*/ -static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { unsigned long flags; - reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + reg_gio_rw_pa_dout dout; + struct nand_chip *this = mtd->priv; local_irq_save(flags); - switch(cmd){ - case NAND_CTL_SETCLE: - dout.data |= (1<<CLE_BIT); - break; - case NAND_CTL_CLRCLE: - dout.data &= ~(1<<CLE_BIT); - break; - case NAND_CTL_SETALE: - dout.data |= (1<<ALE_BIT); - break; - case NAND_CTL_CLRALE: - dout.data &= ~(1<<ALE_BIT); - break; - case NAND_CTL_SETNCE: - dout.data |= (1<<CE_BIT); - break; - case NAND_CTL_CLRNCE: - dout.data &= ~(1<<CE_BIT); - break; + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(gio, regi_gio, rw_pa_dout); + dout.data &= ~PIN_BITMASK; + +#if (CE_BIT == 4 && NAND_NCE == 1 && \ + CLE_BIT == 5 && NAND_CLE == 2 && \ + ALE_BIT == 6 && NAND_ALE == 4) + /* Pins in same order as control bits, but shifted. + * Optimize for this case; works for 2.6.18 */ + dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT; +#else + /* the slow way */ + if (!(ctrl & NAND_NCE)) + dout.data |= (1 << CE_BIT); + if (ctrl & NAND_CLE) + dout.data |= (1 << CLE_BIT); + if (ctrl & NAND_ALE) + dout.data |= (1 << ALE_BIT); +#endif + REG_WR(gio, regi_gio, rw_pa_dout, dout); } - REG_WR(gio, regi_gio, rw_pa_dout, dout); + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + local_irq_restore(flags); } /* * read device ready pin */ -int crisv32_device_ready(struct mtd_info *mtd) +static int crisv32_device_ready(struct mtd_info *mtd) { reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din); return ((din.data & (1 << BY_BIT)) >> BY_BIT); @@ -78,21 +97,23 @@ int crisv32_device_ready(struct mtd_info *mtd) /* * Main initialization routine */ -struct mtd_info* __init crisv32_nand_flash_probe (void) +struct mtd_info *__init crisv32_nand_flash_probe(void) { void __iomem *read_cs; void __iomem *write_cs; - reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg); + reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, + rw_grp3_cfg); reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe); + struct mtd_info_wrapper *wrapper; struct nand_chip *this; int err = 0; /* Allocate memory for MTD device structure and private data */ - crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), - GFP_KERNEL); - if (!crisv32_mtd) { - printk ("Unable to allocate CRISv32 NAND MTD device structure.\n"); + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); err = -ENOMEM; return NULL; } @@ -101,45 +122,42 @@ struct mtd_info* __init crisv32_nand_flash_probe (void) write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192); if (!read_cs || !write_cs) { - printk("CRISv32 NAND ioremap failed\n"); + printk(KERN_ERR "CRISv32 NAND ioremap failed\n"); err = -EIO; goto out_mtd; } /* Get pointer to private data */ - this = (struct nand_chip *) (&crisv32_mtd[1]); + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; pa_oe.oe |= 1 << CE_BIT; pa_oe.oe |= 1 << ALE_BIT; pa_oe.oe |= 1 << CLE_BIT; - pa_oe.oe &= ~ (1 << BY_BIT); + pa_oe.oe &= ~(1 << BY_BIT); REG_WR(gio, regi_gio, rw_pa_oe, pa_oe); bif_cfg.gated_csp0 = regk_bif_core_rd; bif_cfg.gated_csp1 = regk_bif_core_wr; REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg); - /* Initialize structures */ - memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info)); - memset((char *) this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ crisv32_mtd->priv = this; /* Set address of NAND IO lines */ this->IO_ADDR_R = read_cs; this->IO_ADDR_W = write_cs; - this->hwcontrol = crisv32_hwcontrol; + this->cmd_ctrl = crisv32_hwcontrol; this->dev_ready = crisv32_device_ready; /* 20 us command delay time */ this->chip_delay = 20; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - this->options = NAND_USE_FLASH_BBT; + /* this->options = NAND_USE_FLASH_BBT; */ - /* Scan to find existence of the device */ - if (nand_scan (crisv32_mtd, 1)) { + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { err = -ENXIO; goto out_ior; } @@ -150,7 +168,7 @@ out_ior: iounmap((void *)read_cs); iounmap((void *)write_cs); out_mtd: - kfree (crisv32_mtd); - return NULL; + kfree(wrapper); + return NULL; } diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c index 6dbd700d3d6..53db3870ba0 100644 --- a/arch/cris/arch-v32/drivers/pcf8563.c +++ b/arch/cris/arch-v32/drivers/pcf8563.c @@ -10,7 +10,7 @@ * 400 kbits/s. The built-in word address register is incremented * automatically after each written or read byte. * - * Copyright (c) 2002-2003, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. @@ -26,6 +26,7 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -37,24 +38,27 @@ #define PCF8563_MAJOR 121 /* Local major number. */ #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.1 $" +#define DRIVER_VERSION "$Revision: 1.17 $" /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -int pcf8563_open(struct inode *, struct file *); -int pcf8563_release(struct inode *, struct file *); + +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, - .ioctl = pcf8563_ioctl, - .open = pcf8563_open, - .release = pcf8563_release, + .ioctl = pcf8563_ioctl }; unsigned char @@ -62,7 +66,7 @@ pcf8563_readreg(int reg) { unsigned char res = rtc_read(reg); - /* The PCF8563 does not return 0 for unimplemented bits */ + /* The PCF8563 does not return 0 for unimplemented bits. */ switch (reg) { case RTC_SECONDS: case RTC_MINUTES: @@ -95,11 +99,6 @@ pcf8563_readreg(int reg) void pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } @@ -114,11 +113,13 @@ get_rtc_time(struct rtc_time *tm) tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); tm->tm_sec &= 0x7F; tm->tm_min &= 0x7F; tm->tm_hour &= 0x3F; @@ -137,8 +138,19 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + /* Initiate the i2c protocol. */ - i2c_init(); + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; + } /* * First of all we need to reset the chip. This is done by @@ -170,24 +182,20 @@ pcf8563_init(void) if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); - return -1; + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - - return 0; + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -200,8 +208,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -211,125 +219,147 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { - return -EFAULT; - } - - return 0; + case RTC_RD_TIME: + { + struct rtc_time tm; + + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; } - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) return -EPERM; -#else - int leap; - int year; - int century; - struct rtc_time tm; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - /* - * Check if tm.tm_year is a leap year. A year is a leap - * year if it is divisible by 4 but not 100, except - * that years divisible by 400 _are_ leap years. - */ - year = tm.tm_year; - leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_wday >= 7) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - case RTC_VLOW_RD: - { - int vl_bit = 0; + if (copy_from_user(&tm, (struct rtc_time *) arg, + sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; - return 0; - } + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + int ret = rtc_read(RTC_SECONDS); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + rtc_write(RTC_SECONDS, (ret & 0x7F)); - return 0; - } + /* Clear the cached value. */ + voltage_low = 0; - default: - return -ENOTTY; + return 0; + } + default: + return -ENOTTY; } return 0; } -int -pcf8563_open(struct inode *inode, struct file *filp) +static int __init pcf8563_register(void) { - return 0; -} + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC " + "device.\n", PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } -int -pcf8563_release(struct inode *inode, struct file *filp) -{ return 0; } -module_init(pcf8563_init); +module_init(pcf8563_register); module_exit(pcf8563_exit); diff --git a/arch/cris/arch-v32/drivers/sync_serial.c b/arch/cris/arch-v32/drivers/sync_serial.c index d581b0a92a3..47c377df6fb 100644 --- a/arch/cris/arch-v32/drivers/sync_serial.c +++ b/arch/cris/arch-v32/drivers/sync_serial.c @@ -1,5 +1,5 @@ /* - * Simple synchronous serial port driver for ETRAX FS. + * Simple synchronous serial port driver for ETRAX FS and Artpec-3. * * Copyright (c) 2005 Axis Communications AB * @@ -21,17 +21,18 @@ #include <linux/spinlock.h> #include <asm/io.h> -#include <asm/arch/dma.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/sser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/hwregs/dma.h> -#include <asm/arch/hwregs/intr_vect_defs.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/reg_map.h> +#include <dma.h> +#include <pinmux.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/sser_defs.h> +#include <hwregs/dma_defs.h> +#include <hwregs/dma.h> +#include <hwregs/intr_vect_defs.h> +#include <hwregs/intr_vect.h> +#include <hwregs/reg_map.h> #include <asm/sync_serial.h> + /* The receiver is a bit tricky beacuse of the continuous stream of data.*/ /* */ /* Three DMA descriptors are linked together. Each DMA descriptor is */ @@ -63,8 +64,10 @@ /* words can be handled */ #define IN_BUFFER_SIZE 12288 #define IN_DESCR_SIZE 256 -#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) -#define OUT_BUFFER_SIZE 4096 +#define NBR_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) + +#define OUT_BUFFER_SIZE 1024*8 +#define NBR_OUT_DESCR 8 #define DEFAULT_FRAME_RATE 0 #define DEFAULT_WORD_RATE 7 @@ -78,6 +81,8 @@ #define DEBUGPOLL(x) #define DEBUGRXINT(x) #define DEBUGTXINT(x) +#define DEBUGTRDMA(x) +#define DEBUGOUTBUF(x) typedef struct sync_port { @@ -97,10 +102,11 @@ typedef struct sync_port int output; int input; - volatile unsigned int out_count; /* Remaining bytes for current transfer */ - unsigned char* outp; /* Current position in out_buffer */ - volatile unsigned char* volatile readp; /* Next byte to be read by application */ - volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + unsigned int in_buffer_size; unsigned int inbufchunk; unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); @@ -108,11 +114,30 @@ typedef struct sync_port unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); struct dma_descr_data* next_rx_desc; struct dma_descr_data* prev_rx_desc; + + /* Pointer to the first available descriptor in the ring, + * unless active_tr_descr == catch_tr_descr and a dma + * transfer is active */ + struct dma_descr_data *active_tr_descr; + + /* Pointer to the first allocated descriptor in the ring */ + struct dma_descr_data *catch_tr_descr; + + /* Pointer to the descriptor with the current end-of-list */ + struct dma_descr_data *prev_tr_descr; int full; - dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); + /* Pointer to the first byte being read by DMA + * or current position in out_buffer if not using DMA. */ + unsigned char *out_rd_ptr; + + /* Number of bytes currently locked for being read by DMA */ + int out_buf_count; + + dma_descr_data in_descr[NBR_IN_DESCR] __attribute__ ((__aligned__(16))); dma_descr_context in_context __attribute__ ((__aligned__(32))); - dma_descr_data out_descr __attribute__ ((__aligned__(16))); + dma_descr_data out_descr[NBR_OUT_DESCR] + __attribute__ ((__aligned__(16))); dma_descr_context out_context __attribute__ ((__aligned__(32))); wait_queue_head_t out_wait_q; wait_queue_head_t in_wait_q; @@ -143,11 +168,11 @@ static ssize_t sync_serial_read(struct file *file, char *buf, #endif static void send_word(sync_port* port); -static void start_dma(struct sync_port *port, const char* data, int count); +static void start_dma_out(struct sync_port *port, const char *data, int count); static void start_dma_in(sync_port* port); #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); #endif #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ @@ -157,22 +182,49 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); #define SYNC_SER_MANUAL #endif #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +#ifdef CONFIG_ETRAXFS /* ETRAX FS */ +#define OUT_DMA_NBR 4 +#define IN_DMA_NBR 5 +#define PINMUX_SSER pinmux_sser0 +#define SYNCSER_INST regi_sser0 +#define SYNCSER_INTR_VECT SSER0_INTR_VECT +#define OUT_DMA_INST regi_dma4 +#define IN_DMA_INST regi_dma5 +#define DMA_OUT_INTR_VECT DMA4_INTR_VECT +#define DMA_IN_INTR_VECT DMA5_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser0 +#else /* Artpec-3 */ +#define OUT_DMA_NBR 6 +#define IN_DMA_NBR 7 +#define PINMUX_SSER pinmux_sser +#define SYNCSER_INST regi_sser +#define SYNCSER_INTR_VECT SSER_INTR_VECT +#define OUT_DMA_INST regi_dma6 +#define IN_DMA_INST regi_dma7 +#define DMA_OUT_INTR_VECT DMA6_INTR_VECT +#define DMA_IN_INTR_VECT DMA7_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser #endif /* The ports */ static struct sync_port ports[]= { { - .regi_sser = regi_sser0, - .regi_dmaout = regi_dma4, - .regi_dmain = regi_dma5, + .regi_sser = SYNCSER_INST, + .regi_dmaout = OUT_DMA_INST, + .regi_dmain = IN_DMA_INST, #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) .use_dma = 1, #else .use_dma = 0, #endif - }, + } +#ifdef CONFIG_ETRAXFS + , + { .regi_sser = regi_sser1, .regi_dmaout = regi_dma6, @@ -183,9 +235,10 @@ static struct sync_port ports[]= .use_dma = 0, #endif } +#endif }; -#define NUMBER_OF_PORTS ARRAY_SIZE(ports) +#define NBR_PORTS ARRAY_SIZE(ports) static const struct file_operations sync_serial_fops = { .owner = THIS_MODULE, @@ -200,19 +253,21 @@ static const struct file_operations sync_serial_fops = { static int __init etrax_sync_serial_init(void) { ports[0].enabled = 0; +#ifdef CONFIG_ETRAXFS ports[1].enabled = 0; - - if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) - { - printk("unable to get major for synchronous serial port\n"); +#endif + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { + printk(KERN_WARNING + "Unable to get major for synchronous serial port\n"); return -EBUSY; } /* Initialize Ports */ #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) - if (crisv32_pinmux_alloc_fixed(pinmux_sser0)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(PINMUX_SSER)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[0].enabled = 1; @@ -220,33 +275,40 @@ static int __init etrax_sync_serial_init(void) #endif #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) - if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[1].enabled = 1; initialize_port(1); #endif - printk("ETRAX FS synchronous serial port driver\n"); +#ifdef CONFIG_ETRAXFS + printk(KERN_INFO "ETRAX FS synchronous serial port driver\n"); +#else + printk(KERN_INFO "Artpec-3 synchronous serial port driver\n"); +#endif return 0; } static void __init initialize_port(int portnbr) { - struct sync_port* port = &ports[portnbr]; + int __attribute__((unused)) i; + struct sync_port *port = &ports[portnbr]; reg_sser_rw_cfg cfg = {0}; reg_sser_rw_frm_cfg frm_cfg = {0}; reg_sser_rw_tr_cfg tr_cfg = {0}; reg_sser_rw_rec_cfg rec_cfg = {0}; - DEBUG(printk("Init sync serial port %d\n", portnbr)); + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); port->port_nbr = portnbr; port->init_irqs = 1; - port->outp = port->out_buffer; + port->out_rd_ptr = port->out_buffer; + port->out_buf_count = 0; + port->output = 1; port->input = 0; @@ -255,7 +317,7 @@ static void __init initialize_port(int portnbr) port->in_buffer_size = IN_BUFFER_SIZE; port->inbufchunk = IN_DESCR_SIZE; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR-1]; port->prev_rx_desc->eol = 1; init_waitqueue_head(&port->out_wait_q); @@ -286,8 +348,13 @@ static void __init initialize_port(int portnbr) tr_cfg.sample_size = 7; tr_cfg.sh_dir = regk_sser_msbfirst; tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; +#if 0 tr_cfg.rate_ctrl = regk_sser_bulk; tr_cfg.data_pin_use = regk_sser_dout; +#else + tr_cfg.rate_ctrl = regk_sser_iso; + tr_cfg.data_pin_use = regk_sser_dout; +#endif tr_cfg.bulk_wspace = 1; REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -296,6 +363,27 @@ static void __init initialize_port(int portnbr) rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; rec_cfg.fifo_thr = regk_sser_inf; REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + +#ifdef SYNC_SER_DMA + /* Setup the descriptor ring for dma out/transmit. */ + for (i = 0; i < NBR_OUT_DESCR; i++) { + port->out_descr[i].wait = 0; + port->out_descr[i].intr = 1; + port->out_descr[i].eol = 0; + port->out_descr[i].out_eop = 0; + port->out_descr[i].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[i+1]); + } + + /* Create a ring from the list. */ + port->out_descr[NBR_OUT_DESCR-1].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[0]); + + /* Setup context for traversing the ring. */ + port->active_tr_descr = &port->out_descr[0]; + port->prev_tr_descr = &port->out_descr[NBR_OUT_DESCR-1]; + port->catch_tr_descr = &port->out_descr[0]; +#endif } static inline int sync_data_avail(struct sync_port *port) @@ -311,7 +399,7 @@ static inline int sync_data_avail(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->in_buffer_size - (start - end); @@ -331,7 +419,7 @@ static inline int sync_data_avail_to_end(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->flip + port->in_buffer_size - start; @@ -341,66 +429,69 @@ static inline int sync_data_avail_to_end(struct sync_port *port) static int sync_serial_open(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; - DEBUG(printk("Open sync serial port %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { - DEBUG(printk("Invalid minor %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; /* Allow open this device twice (assuming one reader and one writer) */ if (port->busy == 2) { - DEBUG(printk("Device is busy.. \n")); + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); return -EBUSY; } + + if (port->init_irqs) { if (port->use_dma) { - if (port == &ports[0]){ + if (port == &ports[0]) { #ifdef SYNC_SER_DMA - if(request_irq(DMA4_INTR_VECT, - tr_interrupt, - 0, - "synchronous serial 0 dma tr", - &ports[0])) { + if (request_irq(DMA_OUT_INTR_VECT, + tr_interrupt, + 0, + "synchronous serial 0 dma tr", + &ports[0])) { printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if(request_irq(DMA5_INTR_VECT, - rx_interrupt, - 0, - "synchronous serial 1 dma rx", - &ports[0])) { - free_irq(DMA4_INTR_VECT, &port[0]); + } else if (request_irq(DMA_IN_INTR_VECT, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR, - "synchronous serial 0 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(OUT_DMA_NBR, + "synchronous serial 0 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR, - "synchronous serial 0 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - crisv32_free_dma(SYNC_SER0_TX_DMA_NBR); - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(IN_DMA_NBR, + "synchronous serial 0 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + crisv32_free_dma(OUT_DMA_NBR); + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); return -EBUSY; } #endif } - else if (port == &ports[1]){ +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { #ifdef SYNC_SER_DMA if (request_irq(DMA6_INTR_VECT, tr_interrupt, @@ -417,20 +508,22 @@ static int sync_serial_open(struct inode *inode, struct file *file) free_irq(DMA6_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR, - "synchronous serial 1 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { - free_irq(21, &ports[1]); - free_irq(20, &ports[1]); + } else if (crisv32_request_dma( + SYNC_SER1_TX_DMA_NBR, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { + free_irq(DMA6_INTR_VECT, &ports[1]); + free_irq(DMA7_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR, - "synchronous serial 3 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { + } else if (crisv32_request_dma( + SYNC_SER1_RX_DMA_NBR, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { crisv32_free_dma(SYNC_SER1_TX_DMA_NBR); free_irq(DMA6_INTR_VECT, &ports[1]); free_irq(DMA7_INTR_VECT, &ports[1]); @@ -439,14 +532,14 @@ static int sync_serial_open(struct inode *inode, struct file *file) } #endif } - +#endif /* Enable DMAs */ REG_WR(dma, port->regi_dmain, rw_cfg, cfg); REG_WR(dma, port->regi_dmaout, rw_cfg, cfg); /* Enable DMA IRQs */ REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask); REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask); - /* Set up wordsize = 2 for DMAs. */ + /* Set up wordsize = 1 for DMAs. */ DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1); DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1); @@ -455,7 +548,7 @@ static int sync_serial_open(struct inode *inode, struct file *file) } else { /* !port->use_dma */ #ifdef SYNC_SER_MANUAL if (port == &ports[0]) { - if (request_irq(SSER0_INTR_VECT, + if (request_irq(SYNCSER_INTR_VECT, manual_interrupt, 0, "synchronous serial manual irq", @@ -463,7 +556,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) printk("Can't allocate sync serial manual irq"); return -EBUSY; } - } else if (port == &ports[1]) { + } +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { if (request_irq(SSER1_INTR_VECT, manual_interrupt, 0, @@ -473,11 +568,13 @@ static int sync_serial_open(struct inode *inode, struct file *file) return -EBUSY; } } +#endif port->init_irqs = 0; #else panic("sync_serial: Manual mode not supported.\n"); #endif /* SYNC_SER_MANUAL */ } + } /* port->init_irqs */ port->busy++; @@ -487,9 +584,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) static int sync_serial_release(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -506,17 +603,37 @@ static unsigned int sync_serial_poll(struct file *file, poll_table *wait) { int dev = iminor(file->f_path.dentry->d_inode); unsigned int mask = 0; - sync_port* port; + sync_port *port; DEBUGPOLL( static unsigned int prev_mask = 0; ); port = &ports[dev]; + + if (!port->started) { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_rec_cfg rec_cfg = + REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + rec_cfg.rec_en = port->input; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + poll_wait(file, &port->out_wait_q, wait); poll_wait(file, &port->in_wait_q, wait); - /* Some room to write */ - if (port->out_count < OUT_BUFFER_SIZE) + + /* No active transfer, descriptors are available */ + if (port->output && !port->tr_running) + mask |= POLLOUT | POLLWRNORM; + + /* Descriptor and buffer space available. */ + if (port->output && + port->active_tr_descr != port->catch_tr_descr && + port->out_buf_count < OUT_BUFFER_SIZE) mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ - if (sync_data_avail(port) >= port->inbufchunk) + if (port->input && sync_data_avail(port) >= port->inbufchunk) mask |= POLLIN | POLLRDNORM; DEBUGPOLL(if (mask != prev_mask) @@ -531,15 +648,16 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int return_val = 0; + int dma_w_size = regk_dma_set_w_size1; int dev = iminor(file->f_path.dentry->d_inode); - sync_port* port; + sync_port *port; reg_sser_rw_tr_cfg tr_cfg; reg_sser_rw_rec_cfg rec_cfg; reg_sser_rw_frm_cfg frm_cfg; reg_sser_rw_cfg gen_cfg; reg_sser_rw_intr_mask intr_mask; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -1; @@ -558,61 +676,81 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case SSP_SPEED: if (GET_SPEED(arg) == CODEC) { + unsigned int freq; + gen_cfg.base_freq = regk_sser_f32; - /* FREQ = 0 => 4 MHz => clk_div = 7*/ - gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg)); - } - else - { + + /* Clock divider will internally be + * gen_cfg.clk_div + 1. + */ + + freq = GET_FREQ(arg); + switch (freq) { + case FREQ_32kHz: + case FREQ_64kHz: + case FREQ_128kHz: + case FREQ_256kHz: + gen_cfg.clk_div = 125 * + (1 << (freq - FREQ_256kHz)) - 1; + break; + case FREQ_512kHz: + gen_cfg.clk_div = 62; + break; + case FREQ_1MHz: + case FREQ_2MHz: + case FREQ_4MHz: + gen_cfg.clk_div = 8 * (1 << freq) - 1; + break; + } + } else { gen_cfg.base_freq = regk_sser_f29_493; - switch (GET_SPEED(arg)) - { - case SSP150: - gen_cfg.clk_div = 29493000 / (150 * 8) - 1; - break; - case SSP300: - gen_cfg.clk_div = 29493000 / (300 * 8) - 1; - break; - case SSP600: - gen_cfg.clk_div = 29493000 / (600 * 8) - 1; - break; - case SSP1200: - gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; - break; - case SSP2400: - gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; - break; - case SSP4800: - gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; - break; - case SSP9600: - gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; - break; - case SSP19200: - gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; - break; - case SSP28800: - gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; - break; - case SSP57600: - gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; - break; - case SSP115200: - gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; - break; - case SSP230400: - gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; - break; - case SSP460800: - gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; - break; - case SSP921600: - gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; - break; - case SSP3125000: - gen_cfg.base_freq = regk_sser_f100; - gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; - break; + switch (GET_SPEED(arg)) { + case SSP150: + gen_cfg.clk_div = 29493000 / (150 * 8) - 1; + break; + case SSP300: + gen_cfg.clk_div = 29493000 / (300 * 8) - 1; + break; + case SSP600: + gen_cfg.clk_div = 29493000 / (600 * 8) - 1; + break; + case SSP1200: + gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; + break; + case SSP2400: + gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; + break; + case SSP4800: + gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; + break; + case SSP9600: + gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; + break; + case SSP19200: + gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; + break; + case SSP28800: + gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; + break; + case SSP57600: + gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; + break; + case SSP115200: + gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; + break; + case SSP230400: + gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; + break; + case SSP460800: + gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; + break; + case SSP921600: + gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; + break; + case SSP3125000: + gen_cfg.base_freq = regk_sser_f100; + gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; + break; } } @@ -625,46 +763,60 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case MASTER_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.out_on = regk_sser_tr; + frm_cfg.frame_pin_dir = regk_sser_out; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; default: spin_unlock_irq(&port->lock); return -EINVAL; - } if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)) intr_mask.rdav = regk_sser_yes; break; case SSP_FRAME_SYNC: - if (arg & NORMAL_SYNC) + if (arg & NORMAL_SYNC) { + frm_cfg.rec_delay = 1; frm_cfg.tr_delay = 1; + } else if (arg & EARLY_SYNC) - frm_cfg.tr_delay = 0; + frm_cfg.rec_delay = frm_cfg.tr_delay = 0; + else if (arg & SECOND_WORD_SYNC) { + frm_cfg.rec_delay = 7; + frm_cfg.tr_delay = 1; + } tr_cfg.bulk_wspace = frm_cfg.tr_delay; frm_cfg.early_wend = regk_sser_yes; @@ -680,9 +832,11 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & SYNC_OFF) frm_cfg.frame_pin_use = regk_sser_gio0; - if (arg & WORD_SIZE_8) + dma_w_size = regk_dma_set_w_size2; + if (arg & WORD_SIZE_8) { rec_cfg.sample_size = tr_cfg.sample_size = 7; - else if (arg & WORD_SIZE_12) + dma_w_size = regk_dma_set_w_size1; + } else if (arg & WORD_SIZE_12) rec_cfg.sample_size = tr_cfg.sample_size = 11; else if (arg & WORD_SIZE_16) rec_cfg.sample_size = tr_cfg.sample_size = 15; @@ -696,10 +850,13 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & BIT_ORDER_LSB) rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst; - if (arg & FLOW_CONTROL_ENABLE) + if (arg & FLOW_CONTROL_ENABLE) { + frm_cfg.status_pin_use = regk_sser_frm; rec_cfg.fifo_thr = regk_sser_thr16; - else if (arg & FLOW_CONTROL_DISABLE) + } else if (arg & FLOW_CONTROL_DISABLE) { + frm_cfg.status_pin_use = regk_sser_gio0; rec_cfg.fifo_thr = regk_sser_inf; + } if (arg & CLOCK_NOT_GATED) gen_cfg.gate_clk = regk_sser_no; @@ -726,9 +883,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, break; case SSP_OPOLARITY: if (arg & CLOCK_NORMAL) - gen_cfg.out_clk_pol = regk_sser_neg; - else if (arg & CLOCK_INVERT) gen_cfg.out_clk_pol = regk_sser_pos; + else if (arg & CLOCK_INVERT) + gen_cfg.out_clk_pol = regk_sser_neg; if (arg & FRAME_NORMAL) frm_cfg.level = regk_sser_pos_hi; @@ -770,10 +927,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, } - if (port->started) - { - tr_cfg.tr_en = port->output; + if (port->started) { rec_cfg.rec_en = port->input; + gen_cfg.en = (port->output | port->input); } REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -782,138 +938,145 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + + if (cmd == SSP_FRAME_SYNC && (arg & (WORD_SIZE_8 | WORD_SIZE_12 | + WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) { + int en = gen_cfg.en; + gen_cfg.en = 0; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + /* ##### Should DMA be stoped before we change dma size? */ + DMA_WR_CMD(port->regi_dmain, dma_w_size); + DMA_WR_CMD(port->regi_dmaout, dma_w_size); + gen_cfg.en = en; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + } + spin_unlock_irq(&port->lock); return return_val; } -static ssize_t sync_serial_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) +/* NOTE: sync_serial_write does not support concurrency */ +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { int dev = iminor(file->f_path.dentry->d_inode); DECLARE_WAITQUEUE(wait, current); - sync_port *port; - unsigned long c, c1; - unsigned long free_outp; - unsigned long outp; - unsigned long out_buffer; + struct sync_port *port; + int trunc_count; unsigned long flags; + int bytes_free; + int out_buf_count; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { + unsigned char *rd_ptr; /* First allocated byte in the buffer */ + unsigned char *wr_ptr; /* First free byte in the buffer */ + unsigned char *buf_stop_ptr; /* Last byte + 1 */ + + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; - DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); - /* Space to end of buffer */ - /* - * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE - * outp^ +out_count - ^free_outp - * out_buffer 45<- c ->0123OUT_BUFFER_SIZE - * +out_count outp^ - * free_outp - * + /* |<- OUT_BUFFER_SIZE ->| + * |<- out_buf_count ->| + * |<- trunc_count ->| ...->| + * ______________________________________________________ + * | free | data | free | + * |_________|___________________|________________________| + * ^ rd_ptr ^ wr_ptr */ + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu a: %p c: %p\n", + port->port_nbr, count, port->active_tr_descr, + port->catch_tr_descr)); /* Read variables that may be updated by interrupts */ spin_lock_irqsave(&port->lock, flags); - count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count; - outp = (unsigned long)port->outp; - free_outp = outp + port->out_count; + rd_ptr = port->out_rd_ptr; + out_buf_count = port->out_buf_count; spin_unlock_irqrestore(&port->lock, flags); - out_buffer = (unsigned long)port->out_buffer; - /* Find out where and how much to write */ - if (free_outp >= out_buffer + OUT_BUFFER_SIZE) - free_outp -= OUT_BUFFER_SIZE; - if (free_outp >= outp) - c = out_buffer + OUT_BUFFER_SIZE - free_outp; - else - c = outp - free_outp; - if (c > count) - c = count; + /* Check if resources are available */ + if (port->tr_running && + ((port->use_dma && port->active_tr_descr == port->catch_tr_descr) || + out_buf_count >= OUT_BUFFER_SIZE)) { + DEBUGWRITE(printk(KERN_DEBUG "sser%d full\n", dev)); + return -EAGAIN; + } + + buf_stop_ptr = port->out_buffer + OUT_BUFFER_SIZE; + + /* Determine pointer to the first free byte, before copying. */ + wr_ptr = rd_ptr + out_buf_count; + if (wr_ptr >= buf_stop_ptr) + wr_ptr -= OUT_BUFFER_SIZE; -// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); - if (copy_from_user((void*)free_outp, buf, c)) + /* If we wrap the ring buffer, let the user space program handle it by + * truncating the data. This could be more elegant, small buffer + * fragments may occur. + */ + bytes_free = OUT_BUFFER_SIZE - out_buf_count; + if (wr_ptr + bytes_free > buf_stop_ptr) + bytes_free = buf_stop_ptr - wr_ptr; + trunc_count = (count < bytes_free) ? count : bytes_free; + + if (copy_from_user(wr_ptr, buf, trunc_count)) return -EFAULT; - if (c != count) { - buf += c; - c1 = count - c; - DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1)); - if (copy_from_user((void*)out_buffer, buf, c1)) - return -EFAULT; - } - spin_lock_irqsave(&port->lock, flags); - port->out_count += count; - spin_unlock_irqrestore(&port->lock, flags); + DEBUGOUTBUF(printk(KERN_DEBUG "%-4d + %-4d = %-4d %p %p %p\n", + out_buf_count, trunc_count, + port->out_buf_count, port->out_buffer, + wr_ptr, buf_stop_ptr)); /* Make sure transmitter/receiver is running */ - if (!port->started) - { + if (!port->started) { reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); - reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); cfg.en = regk_sser_yes; - tr_cfg.tr_en = port->output; rec_cfg.rec_en = port->input; REG_WR(sser, port->regi_sser, rw_cfg, cfg); - REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); port->started = 1; } - if (file->f_flags & O_NONBLOCK) { - spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, (unsigned char* volatile )port->outp, c); - } - } - spin_unlock_irqrestore(&port->lock, flags); - DEBUGWRITE(printk("w d%d c %lu NB\n", - port->port_nbr, count)); - return count; + /* Setup wait if blocking */ + if (!(file->f_flags & O_NONBLOCK)) { + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); } - /* Sleep until all sent */ - - add_wait_queue(&port->out_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, port->outp, c); - } + port->out_buf_count += trunc_count; + if (port->use_dma) { + start_dma_out(port, wr_ptr, trunc_count); + } else if (!port->tr_running) { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + intr_mask.trdy = 1; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); } spin_unlock_irqrestore(&port->lock, flags); + + /* Exit if non blocking */ + if (file->f_flags & O_NONBLOCK) { + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu %08x\n", + port->port_nbr, trunc_count, + REG_RD_INT(dma, port->regi_dmaout, r_intr))); + return trunc_count; + } + schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) - { return -EINTR; - } - DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count)); - return count; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", + port->port_nbr, trunc_count)); + return trunc_count; } static ssize_t sync_serial_read(struct file * file, char * buf, @@ -926,7 +1089,7 @@ static ssize_t sync_serial_read(struct file * file, char * buf, unsigned char* end; unsigned long flags; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -949,7 +1112,6 @@ static ssize_t sync_serial_read(struct file * file, char * buf, port->started = 1; } - /* Calculate number of available bytes */ /* Save pointers to avoid that they are modified by interrupt */ spin_lock_irqsave(&port->lock, flags); @@ -958,16 +1120,14 @@ static ssize_t sync_serial_read(struct file * file, char * buf, spin_unlock_irqrestore(&port->lock, flags); while ((start == end) && !port->full) /* No data */ { + DEBUGREAD(printk(KERN_DEBUG "&")); if (file->f_flags & O_NONBLOCK) - { return -EAGAIN; - } interruptible_sleep_on(&port->in_wait_q); if (signal_pending(current)) - { return -EINTR; - } + spin_lock_irqsave(&port->lock, flags); start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ @@ -1004,83 +1164,105 @@ static void send_word(sync_port* port) switch(tr_cfg.sample_size) { case 8: - port->out_count--; - tr_data.data = *port->outp++; + port->out_buf_count--; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 12: { - int data = (*port->outp++) << 8; - data |= *port->outp++; - port->out_count-=2; + int data = (*port->out_rd_ptr++) << 8; + data |= *port->out_rd_ptr++; + port->out_buf_count -= 2; tr_data.data = data; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; } break; case 16: - port->out_count-=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 24: - port->out_count-=3; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 3; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *port->outp++; + port->out_rd_ptr += 2; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 32: - port->out_count-=4; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 4; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_rd_ptr += 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; } } - -static void start_dma(struct sync_port* port, const char* data, int count) +static void start_dma_out(struct sync_port *port, + const char *data, int count) { - port->tr_running = 1; - port->out_descr.buf = (char*)virt_to_phys((char*)data); - port->out_descr.after = port->out_descr.buf + count; - port->out_descr.eol = port->out_descr.intr = 1; + port->active_tr_descr->buf = (char *) virt_to_phys((char *) data); + port->active_tr_descr->after = port->active_tr_descr->buf + count; + port->active_tr_descr->intr = 1; + + port->active_tr_descr->eol = 1; + port->prev_tr_descr->eol = 0; + + DEBUGTRDMA(printk(KERN_DEBUG "Inserting eolr:%p eol@:%p\n", + port->prev_tr_descr, port->active_tr_descr)); + port->prev_tr_descr = port->active_tr_descr; + port->active_tr_descr = phys_to_virt((int) port->active_tr_descr->next); + + if (!port->tr_running) { + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, + rw_tr_cfg); - port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr); - port->out_context.saved_data_buf = port->out_descr.buf; + port->out_context.next = 0; + port->out_context.saved_data = + (dma_descr_data *)virt_to_phys(port->prev_tr_descr); + port->out_context.saved_data_buf = port->prev_tr_descr->buf; + + DMA_START_CONTEXT(port->regi_dmaout, + virt_to_phys((char *)&port->out_context)); + + tr_cfg.tr_en = regk_sser_yes; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + DEBUGTRDMA(printk(KERN_DEBUG "dma s\n");); + } else { + DMA_CONTINUE_DATA(port->regi_dmaout); + DEBUGTRDMA(printk(KERN_DEBUG "dma c\n");); + } - DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context)); - DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); + port->tr_running = 1; } -static void start_dma_in(sync_port* port) +static void start_dma_in(sync_port *port) { int i; - char* buf; + char *buf; port->writep = port->flip; - if (port->writep > port->flip + port->in_buffer_size) - { + if (port->writep > port->flip + port->in_buffer_size) { panic("Offset too large in sync serial driver\n"); return; } buf = (char*)virt_to_phys(port->in_buffer); - for (i = 0; i < NUM_IN_DESCR; i++) { + for (i = 0; i < NBR_IN_DESCR; i++) { port->in_descr[i].buf = buf; port->in_descr[i].after = buf + port->inbufchunk; port->in_descr[i].intr = 1; @@ -1092,59 +1274,126 @@ static void start_dma_in(sync_port* port) port->in_descr[i-1].next = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_descr[i-1].eol = regk_sser_yes; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR - 1]; port->in_context.saved_data = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_context.saved_data_buf = port->in_descr[0].buf; DMA_START_CONTEXT(port->regi_dmain, virt_to_phys(&port->in_context)); } #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t tr_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; + reg_dma_rw_stat stat; int i; - struct dma_descr_data *descr; - unsigned int sentl; int found = 0; + int stop_sser = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) - { + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; - if (!port->enabled || !port->use_dma ) + if (!port->enabled || !port->use_dma) continue; + /* IRQ active for the port? */ masked = REG_RD(dma, port->regi_dmaout, r_masked_intr); + if (!masked.data) + continue; - if (masked.data) /* IRQ active for the port? */ - { - found = 1; - /* Clear IRQ */ - REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); - descr = &port->out_descr; - sentl = descr->after - descr->buf; - port->out_count -= sentl; - port->outp += sentl; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; - if (port->out_count) { - int c; - c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; - if (c > port->out_count) - c = port->out_count; - DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); - start_dma(port, port->outp, c); - } else { - DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); - port->tr_running = 0; + found = 1; + + /* Check if we should stop the DMA transfer */ + stat = REG_RD(dma, port->regi_dmaout, rw_stat); + if (stat.list_state == regk_dma_data_at_eol) + stop_sser = 1; + + /* Clear IRQ */ + REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); + + if (!stop_sser) { + /* The DMA has completed a descriptor, EOL was not + * encountered, so step relevant descriptor and + * datapointers forward. */ + int sent; + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGTXINT(printk(KERN_DEBUG "%-4d - %-4d = %-4d\t" + "in descr %p (ac: %p)\n", + port->out_buf_count, sent, + port->out_buf_count - sent, + port->catch_tr_descr, + port->active_tr_descr);); + port->out_buf_count -= sent; + port->catch_tr_descr = + phys_to_virt((int) port->catch_tr_descr->next); + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->buf); + } else { + int i, sent; + /* EOL handler. + * Note that if an EOL was encountered during the irq + * locked section of sync_ser_write the DMA will be + * restarted and the eol flag will be cleared. + * The remaining descriptors will be traversed by + * the descriptor interrupts as usual. + */ + i = 0; + while (!port->catch_tr_descr->eol) { + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "traversing descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + port->out_buf_count -= sent; + port->catch_tr_descr = phys_to_virt( + (int)port->catch_tr_descr->next); + i++; + if (i >= NBR_OUT_DESCR) { + /* TODO: Reset and recover */ + panic("sync_serial: missing eol"); + } } - wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "eol at descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + + port->out_buf_count -= sent; + + /* Update read pointer to first free byte, we + * may already be writing data there. */ + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->after); + if (port->out_rd_ptr > port->out_buffer + + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; + + reg_sser_rw_tr_cfg tr_cfg = + REG_RD(sser, port->regi_sser, rw_tr_cfg); + DEBUGTXINT(printk(KERN_DEBUG + "tr_int DMA stop %d, set catch @ %p\n", + port->out_buf_count, + port->active_tr_descr)); + if (port->out_buf_count != 0) + printk(KERN_CRIT "sync_ser: buffer not " + "empty after eol.\n"); + port->catch_tr_descr = port->active_tr_descr; + port->tr_running = 0; + tr_cfg.tr_en = regk_sser_no; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); } return IRQ_RETVAL(found); } /* tr_interrupt */ -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t rx_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; @@ -1152,7 +1401,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) int i; int found = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; @@ -1166,7 +1415,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) found = 1; while (REG_RD(dma, port->regi_dmain, rw_data) != virt_to_phys(port->next_rx_desc)) { - + DEBUGRXINT(printk(KERN_DEBUG "!")); if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { int first_size = port->flip + port->in_buffer_size - port->writep; memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size); @@ -1185,11 +1434,16 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) port->full = 1; } - port->next_rx_desc->eol = 0; - port->prev_rx_desc->eol = 1; - port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + port->next_rx_desc->eol = 1; + port->prev_rx_desc->eol = 0; + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 0); + port->prev_rx_desc = port->next_rx_desc; port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); - wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 1); + /* wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); DMA_CONTINUE(port->regi_dmain); REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr); @@ -1201,15 +1455,15 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) #endif /* SYNC_SER_DMA */ #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t manual_interrupt(int irq, void *dev_id) { int i; int found = 0; reg_sser_r_masked_intr masked; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { - sync_port* port = &ports[i]; + sync_port *port = &ports[i]; if (!port->enabled || port->use_dma) { @@ -1263,7 +1517,7 @@ static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs if (masked.trdy) /* Transmitter ready? */ { found = 1; - if (port->out_count > 0) /* More data to send */ + if (port->out_buf_count > 0) /* More data to send */ send_word(port); else /* transmission finished */ { diff --git a/arch/cris/arch-v32/kernel/Makefile b/arch/cris/arch-v32/kernel/Makefile index 5d5b613cde8..993d987b007 100644 --- a/arch/cris/arch-v32/kernel/Makefile +++ b/arch/cris/arch-v32/kernel/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $ # # Makefile for the linux kernel. # @@ -6,9 +5,9 @@ extra-y := head.o -obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \ +obj-y := entry.o traps.o irq.o debugport.o \ process.o ptrace.o setup.o signal.o traps.o time.o \ - arbiter.o io.o + cache.o cacheflush.o obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o diff --git a/arch/cris/arch-v32/kernel/arbiter.c b/arch/cris/arch-v32/kernel/arbiter.c deleted file mode 100644 index 420a5312ed0..00000000000 --- a/arch/cris/arch-v32/kernel/arbiter.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Memory arbiter functions. Allocates bandwidth through the - * arbiter and sets up arbiter breakpoints. - * - * The algorithm first assigns slots to the clients that has specified - * bandwidth (e.g. ethernet) and then the remaining slots are divided - * on all the active clients. - * - * Copyright (c) 2004, 2005 Axis Communications AB. - */ - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/arbiter.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <linux/interrupt.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/spinlock.h> -#include <asm/io.h> - -struct crisv32_watch_entry -{ - unsigned long instance; - watch_callback* cb; - unsigned long start; - unsigned long end; - int used; -}; - -#define NUMBER_OF_BP 4 -#define NBR_OF_CLIENTS 14 -#define NBR_OF_SLOTS 64 -#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ -#define INTMEM_BANDWIDTH 400000000 -#define NBR_OF_REGIONS 2 - -static struct crisv32_watch_entry watches[NUMBER_OF_BP] = -{ - {regi_marb_bp0}, - {regi_marb_bp1}, - {regi_marb_bp2}, - {regi_marb_bp3} -}; - -static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; - -DEFINE_SPINLOCK(arbiter_lock); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); - -static void crisv32_arbiter_config(int region) -{ - int slot; - int client; - int interval = 0; - int val[NBR_OF_SLOTS]; - - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - val[slot] = NBR_OF_CLIENTS + 1; - - for (client = 0; client < NBR_OF_CLIENTS; client++) - { - int pos; - if (!requested_slots[region][client]) - continue; - interval = NBR_OF_SLOTS / requested_slots[region][client]; - pos = 0; - while (pos < NBR_OF_SLOTS) - { - if (val[pos] != NBR_OF_CLIENTS + 1) - pos++; - else - { - val[pos] = client; - pos += interval; - } - } - } - - client = 0; - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - { - if (val[slot] == NBR_OF_CLIENTS + 1) - { - int first = client; - while(!active_clients[region][client]) { - client = (client + 1) % NBR_OF_CLIENTS; - if (client == first) - break; - } - val[slot] = client; - client = (client + 1) % NBR_OF_CLIENTS; - } - if (region == EXT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); - else if (region == INT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); - } -} - -extern char _stext, _etext; - -static void crisv32_arbiter_init(void) -{ - static int initialized = 0; - - if (initialized) - return; - - initialized = 1; - - /* CPU caches are active. */ - active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; - crisv32_arbiter_config(EXT_REGION); - crisv32_arbiter_config(INT_REGION); - - if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, - "arbiter", NULL)) - printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); - -#ifndef CONFIG_ETRAX_KGDB - /* Global watch for writes to kernel text segment. */ - crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, - arbiter_all_clients, arbiter_all_write, NULL); -#endif -} - - - -int crisv32_arbiter_allocate_bandwidth(int client, int region, - unsigned long bandwidth) -{ - int i; - int total_assigned = 0; - int total_clients = 0; - int req; - - crisv32_arbiter_init(); - - for (i = 0; i < NBR_OF_CLIENTS; i++) - { - total_assigned += requested_slots[region][i]; - total_clients += active_clients[region][i]; - } - req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); - - if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) - return -ENOMEM; - - active_clients[region][client] = 1; - requested_slots[region][client] = req; - crisv32_arbiter_config(region); - - return 0; -} - -int crisv32_arbiter_watch(unsigned long start, unsigned long size, - unsigned long clients, unsigned long accesses, - watch_callback* cb) -{ - int i; - - crisv32_arbiter_init(); - - if (start > 0x80000000) { - printk("Arbiter: %lX doesn't look like a physical address", start); - return -EFAULT; - } - - spin_lock(&arbiter_lock); - - for (i = 0; i < NUMBER_OF_BP; i++) { - if (!watches[i].used) { - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - watches[i].used = 1; - watches[i].start = start; - watches[i].end = start + size; - watches[i].cb = cb; - - REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start); - REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end); - REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses); - REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients); - - if (i == 0) - intr_mask.bp0 = regk_marb_yes; - else if (i == 1) - intr_mask.bp1 = regk_marb_yes; - else if (i == 2) - intr_mask.bp2 = regk_marb_yes; - else if (i == 3) - intr_mask.bp3 = regk_marb_yes; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - spin_unlock(&arbiter_lock); - - return i; - } - } - spin_unlock(&arbiter_lock); - return -ENOMEM; -} - -int crisv32_arbiter_unwatch(int id) -{ - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - crisv32_arbiter_init(); - - spin_lock(&arbiter_lock); - - if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { - spin_unlock(&arbiter_lock); - return -EINVAL; - } - - memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); - - if (id == 0) - intr_mask.bp0 = regk_marb_no; - else if (id == 1) - intr_mask.bp2 = regk_marb_no; - else if (id == 2) - intr_mask.bp2 = regk_marb_no; - else if (id == 3) - intr_mask.bp3 = regk_marb_no; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - - spin_unlock(&arbiter_lock); - return 0; -} - -extern void show_registers(struct pt_regs *regs); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) -{ - reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); - reg_marb_bp_r_brk_clients r_clients; - reg_marb_bp_r_brk_addr r_addr; - reg_marb_bp_r_brk_op r_op; - reg_marb_bp_r_brk_first_client r_first; - reg_marb_bp_r_brk_size r_size; - reg_marb_bp_rw_ack ack = {0}; - reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; - struct crisv32_watch_entry* watch; - - if (masked_intr.bp0) { - watch = &watches[0]; - ack_intr.bp0 = regk_marb_yes; - } else if (masked_intr.bp1) { - watch = &watches[1]; - ack_intr.bp1 = regk_marb_yes; - } else if (masked_intr.bp2) { - watch = &watches[2]; - ack_intr.bp2 = regk_marb_yes; - } else if (masked_intr.bp3) { - watch = &watches[3]; - ack_intr.bp3 = regk_marb_yes; - } else { - return IRQ_NONE; - } - - /* Retrieve all useful information and print it. */ - r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); - r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); - r_op = REG_RD(marb_bp, watch->instance, r_brk_op); - r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); - r_size = REG_RD(marb_bp, watch->instance, r_brk_size); - - printk("Arbiter IRQ\n"); - printk("Clients %X addr %X op %X first %X size %X\n", - REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); - - REG_WR(marb_bp, watch->instance, rw_ack, ack); - REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); - - printk("IRQ occured at %lX\n", regs->erp); - - if (watch->cb) - watch->cb(); - - - return IRQ_HANDLED; -} diff --git a/arch/cris/arch-v32/kernel/crisksyms.c b/arch/cris/arch-v32/kernel/crisksyms.c index e513da71124..77d02c15a7f 100644 --- a/arch/cris/arch-v32/kernel/crisksyms.c +++ b/arch/cris/arch-v32/kernel/crisksyms.c @@ -2,7 +2,8 @@ #include <linux/irq.h> #include <asm/arch/dma.h> #include <asm/arch/intmem.h> -#include <asm/arch/pinmux.h> +#include <asm/arch/mach/pinmux.h> +#include <asm/arch/io.h> /* Functions for allocating DMA channels */ EXPORT_SYMBOL(crisv32_request_dma); @@ -16,7 +17,11 @@ EXPORT_SYMBOL(crisv32_intmem_virt_to_phys); /* Functions for handling pinmux */ EXPORT_SYMBOL(crisv32_pinmux_alloc); +EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed); EXPORT_SYMBOL(crisv32_pinmux_dealloc); +EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed); +EXPORT_SYMBOL(crisv32_io_get_name); +EXPORT_SYMBOL(crisv32_io_get); /* Functions masking/unmasking interrupts */ EXPORT_SYMBOL(mask_irq); diff --git a/arch/cris/arch-v32/kernel/debugport.c b/arch/cris/arch-v32/kernel/debugport.c index d1272ad9215..15af4c29315 100644 --- a/arch/cris/arch-v32/kernel/debugport.c +++ b/arch/cris/arch-v32/kernel/debugport.c @@ -4,17 +4,12 @@ #include <linux/console.h> #include <linux/init.h> -#include <linux/major.h> -#include <linux/delay.h> -#include <linux/tty.h> #include <asm/system.h> -#include <asm/io.h> -#include <asm/arch/hwregs/ser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/pinmux.h> - -#include <asm/irq.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/dma_defs.h> +#include <asm/arch/mach/pinmux.h> struct dbg_port { @@ -59,45 +54,50 @@ struct dbg_port ports[] = 115200, 'N', 8 - } + }, +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + { + 4, + regi_ser4, + 0, + 115200, + 'N', + 8 + }, +#endif }; static struct dbg_port *port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_DEBUG_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_DEBUG_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_DEBUG_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #ifdef CONFIG_ETRAX_KGDB static struct dbg_port *kgdb_port = #if defined(CONFIG_ETRAX_KGDB_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_KGDB_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_KGDB_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_KGDB_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_KGDB_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #endif -#ifdef CONFIG_ETRAXFS_SIM -extern void print_str( const char *str ); -static char buffer[1024]; -static char msg[] = "Debug: "; -static int buffer_pos = sizeof(msg) - 1; -#endif - -extern struct tty_driver *serial_driver; - static void start_port(struct dbg_port* p) { @@ -114,6 +114,10 @@ start_port(struct dbg_port* p) crisv32_pinmux_alloc_fixed(pinmux_ser2); else if (p->nbr == 3) crisv32_pinmux_alloc_fixed(pinmux_ser3); +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + else if (p->nbr == 4) + crisv32_pinmux_alloc_fixed(pinmux_ser4); +#endif /* Set up serial port registers */ reg_ser_rw_tr_ctrl tr_ctrl = {0}; @@ -156,124 +160,21 @@ start_port(struct dbg_port* p) REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); } -/* No debug */ -#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - return; -} - -/* Target debug */ -#elif !defined(CONFIG_ETRAXFS_SIM) - -static void -console_write_direct(struct console *co, const char *buf, unsigned int len) -{ - int i; - reg_ser_r_stat_din stat; - reg_ser_rw_tr_dma_en tr_dma_en, old; - - /* Switch to manual mode */ - tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); - if (tr_dma_en.en == regk_ser_yes) { - tr_dma_en.en = regk_ser_no; - REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); - } - - /* Send data */ - for (i = 0; i < len; i++) { - /* LF -> CRLF */ - if (buf[i] == '\n') { - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, '\r'); - } - /* Wait until transmitter is ready and send.*/ - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, buf[i]); - } - - /* Restore mode */ - if (tr_dma_en.en != old.en) - REG_WR(ser, port->instance, rw_tr_dma_en, old); -} - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - if (!port) - return; - console_write_direct(co, buf, len); -} - - - -#else - -/* VCS debug */ - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - char* pos; - pos = memchr(buf, '\n', len); - if (pos) { - int l = ++pos - buf; - memcpy(buffer + buffer_pos, buf, l); - memcpy(buffer, msg, sizeof(msg) - 1); - buffer[buffer_pos + l] = '\0'; - print_str(buffer); - buffer_pos = sizeof(msg) - 1; - if (pos - buf != len) { - memcpy(buffer + buffer_pos, pos, len - l); - buffer_pos += len - l; - } - } else { - memcpy(buffer + buffer_pos, buf, len); - buffer_pos += len; - } -} - -#endif - -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write(NULL, buf, strlen(buf)); - return printed_len; -} - -void -stupid_debug(char* buf) -{ - console_write(NULL, buf, strlen(buf)); -} - #ifdef CONFIG_ETRAX_KGDB /* Use polling to get a single character from the kernel debug port */ int getDebugChar(void) { - reg_ser_rs_status_data stat; + reg_ser_rs_stat_din stat; reg_ser_rw_ack_intr ack_intr = { 0 }; do { - stat = REG_RD(ser, kgdb_instance, rs_status_data); - } while (!stat.data_avail); + stat = REG_RD(ser, kgdb_port->instance, rs_stat_din); + } while (!stat.dav); /* Ack the data_avail interrupt. */ - ack_intr.data_avail = 1; - REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); + ack_intr.dav = 1; + REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr); return stat.data; } @@ -282,173 +183,18 @@ getDebugChar(void) void putDebugChar(int val) { - reg_ser_r_status_data stat; + reg_ser_r_stat_din stat; do { - stat = REG_RD (ser, kgdb_instance, r_status_data); - } while (!stat.tr_ready); - REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); + stat = REG_RD(ser, kgdb_port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, kgdb_port->instance, rw_dout, val); } #endif /* CONFIG_ETRAX_KGDB */ -static int __init -console_setup(struct console *co, char *options) -{ - char* s; - - if (options) { - port = &ports[co->index]; - port->baudrate = 115200; - port->parity = 'N'; - port->bits = 8; - port->baudrate = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) port->parity = *s++; - if (*s) port->bits = *s++ - '0'; - port->started = 0; - start_port(port); - } - return 0; -} - -/* This is a dummy serial device that throws away anything written to it. - * This is used when no debug output is wanted. - */ -static struct tty_driver dummy_driver; - -static int dummy_open(struct tty_struct *tty, struct file * filp) -{ - return 0; -} - -static void dummy_close(struct tty_struct *tty, struct file * filp) -{ -} - -static int dummy_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - return count; -} - -static int -dummy_write_room(struct tty_struct *tty) -{ - return 8192; -} - -void __init -init_dummy_console(void) -{ - memset(&dummy_driver, 0, sizeof(struct tty_driver)); - dummy_driver.driver_name = "serial"; - dummy_driver.name = "ttyS"; - dummy_driver.major = TTY_MAJOR; - dummy_driver.minor_start = 68; - dummy_driver.num = 1; /* etrax100 has 4 serial ports */ - dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; - dummy_driver.subtype = SERIAL_TYPE_NORMAL; - dummy_driver.init_termios = tty_std_termios; - dummy_driver.init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ - dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - - dummy_driver.open = dummy_open; - dummy_driver.close = dummy_close; - dummy_driver.write = dummy_write; - dummy_driver.write_room = dummy_write_room; - if (tty_register_driver(&dummy_driver)) - panic("Couldn't register dummy serial driver\n"); -} - -static struct tty_driver* -crisv32_console_device(struct console* co, int *index) -{ - if (port) - *index = port->nbr; - return port ? serial_driver : &dummy_driver; -} - -static struct console sercons = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : -1, - cflag : 0, - next : NULL -}; -static struct console sercons0 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 0, - cflag : 0, - next : NULL -}; - -static struct console sercons1 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 1, - cflag : 0, - next : NULL -}; -static struct console sercons2 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 2, - cflag : 0, - next : NULL -}; -static struct console sercons3 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 3, - cflag : 0, - next : NULL -}; - /* Register console for printk's, etc. */ int __init init_etrax_debug(void) { - static int first = 1; - - if (!first) { - unregister_console(&sercons); - register_console(&sercons0); - register_console(&sercons1); - register_console(&sercons2); - register_console(&sercons3); - init_dummy_console(); - return 0; - } - first = 0; - register_console(&sercons); start_port(port); #ifdef CONFIG_ETRAX_KGDB @@ -456,5 +202,3 @@ init_etrax_debug(void) #endif /* CONFIG_ETRAX_KGDB */ return 0; } - -__initcall(init_etrax_debug); diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S index f9d27807b91..eebbaba4543 100644 --- a/arch/cris/arch-v32/kernel/entry.S +++ b/arch/cris/arch-v32/kernel/entry.S @@ -10,7 +10,7 @@ * after a timer-interrupt and after each system call. * * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. + * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in fork.c:copy_process, signal.c:do_signal, * ptrace.c and ptrace.h @@ -281,12 +281,10 @@ _work_notifysig: ;; Deal with pending signals and notify-resume requests. addoq +TI_flags, $r0, $acr - move.d [$acr], $r13 ; The thread_info_flags parameter. - move.d $r9, $r10 ; do_notify_resume syscall/irq param. - moveq 0, $r11 ; oldset param - 0 in this case. - move.d $sp, $r12 ; The regs param. + move.d [$acr], $r12 ; The thread_info_flags parameter. + move.d $sp, $r11 ; The regs param. jsr do_notify_resume - nop + move.d $r9, $r10 ; do_notify_resume syscall/irq param. ba _Rexit nop @@ -396,7 +394,7 @@ nmi_interrupt: btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0 bpl 1f nop - jsr handle_watchdog_bite ; In time.c. + jsr handle_watchdog_bite ; In time.c. move.d $sp, $r10 ; Pointer to registers 1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0 bpl 1f @@ -515,6 +513,13 @@ _ugdb_handle_exception: ba do_sigtrap ; SIGTRAP the offending process. move.d [$sp+], $r0 ; Restore R0 in delay slot. + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop + .data .section .rodata,"a" @@ -778,21 +783,21 @@ sys_call_table: .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait .long sys_remap_file_pages - .long sys_set_tid_address - .long sys_timer_create - .long sys_timer_settime /* 260 */ - .long sys_timer_gettime - .long sys_timer_getoverrun - .long sys_timer_delete - .long sys_clock_settime - .long sys_clock_gettime /* 265 */ - .long sys_clock_getres - .long sys_clock_nanosleep + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep .long sys_statfs64 .long sys_fstatfs64 .long sys_tgkill /* 270 */ .long sys_utimes - .long sys_fadvise64_64 + .long sys_fadvise64_64 .long sys_ni_syscall /* sys_vserver */ .long sys_ni_syscall /* sys_mbind */ .long sys_ni_syscall /* 275 sys_get_mempolicy */ @@ -805,6 +810,48 @@ sys_call_table: .long sys_mq_getsetattr .long sys_ni_syscall /* reserved for kexec */ .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + .long sys_utimensat /* 320 */ + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime /* * NOTE!! This doesn't have to be exact - we just have diff --git a/arch/cris/arch-v32/kernel/fasttimer.c b/arch/cris/arch-v32/kernel/fasttimer.c index b40551f9f40..2de9d5849ef 100644 --- a/arch/cris/arch-v32/kernel/fasttimer.c +++ b/arch/cris/arch-v32/kernel/fasttimer.c @@ -1,110 +1,9 @@ -/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $ +/* * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX FS - * This may be useful in other OS than Linux so use 2 space indentation... - * - * $Log: fasttimer.c,v $ - * Revision 1.11 2005/01/04 11:15:46 starvik - * Don't share timer IRQ. - * - * Revision 1.10 2004/12/07 09:19:38 starvik - * Corrected includes. - * Use correct interrupt macros. - * - * Revision 1.9 2004/05/14 10:18:58 starvik - * Export fast_timer_list - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.7 2003/07/10 12:06:14 starvik - * Return IRQ_NONE if irq wasn't handled - * - * Revision 1.6 2003/07/04 08:27:49 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/05 10:16:22 johana - * New INTR_VECT macros. - * - * Revision 1.4 2003/06/03 08:49:45 johana - * Fixed typo. - * - * Revision 1.3 2003/06/02 12:51:27 johana - * Now compiles. - * Commented some include files that probably can be removed. - * - * Revision 1.2 2003/06/02 12:09:41 johana - * Ported to ETRAX FS using the trig interrupt instead of timer1. - * - * Revision 1.3 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.2 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.1 2002/11/18 07:58:06 starvik - * Fast timers (from Linux 2.4) - * - * Revision 1.5 2002/10/15 06:21:39 starvik - * Added call to init_waitqueue_head * - * Revision 1.4 2002/05/28 17:47:59 johana - * Added del_fast_timer() - * - * Revision 1.3 2002/05/28 16:16:07 johana - * Handle empty fast_timer_list - * - * Revision 1.2 2002/05/27 15:38:42 johana - * Made it compile without warnings on Linux 2.4. - * (includes, wait_queue, PROC_FS and snprintf) - * - * Revision 1.1 2002/05/27 15:32:25 johana - * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. - * - * Revision 1.8 2001/11/27 13:50:40 pkj - * Disable interrupts while stopping the timer and while modifying the - * list of active timers in timer1_handler() as it may be interrupted - * by other interrupts (e.g., the serial interrupt) which may add fast - * timers. - * - * Revision 1.7 2001/11/22 11:50:32 pkj - * * Only store information about the last 16 timers. - * * proc_fasttimer_read() now uses an allocated buffer, since it - * requires more space than just a page even for only writing the - * last 16 timers. The buffer is only allocated on request, so - * unless /proc/fasttimer is read, it is never allocated. - * * Renamed fast_timer_started to fast_timers_started to match - * fast_timers_added and fast_timers_expired. - * * Some clean-up. - * - * Revision 1.6 2000/12/13 14:02:08 johana - * Removed volatile for fast_timer_list - * - * Revision 1.5 2000/12/13 13:55:35 johana - * Added DEBUG_LOG, added som cli() and cleanup - * - * Revision 1.4 2000/12/05 13:48:50 johana - * Added range check when writing proc file, modified timer int handling - * - * Revision 1.3 2000/11/23 10:10:20 johana - * More debug/logging possibilities. - * Moved GET_JIFFIES_USEC() to timex.h and time.c - * - * Revision 1.2 2000/11/01 13:41:04 johana - * Clean up and bugfixes. - * Created new do_gettimeofday_fast() that gets a timeval struct - * with time based on jiffies and *R_TIMER0_DATA, uses a table - * for fast conversion of timer value to microseconds. - * (Much faster the standard do_gettimeofday() and we don't really - * want to use the true time - we want the "uptime" so timers don't screw up - * when we change the time. - * TODO: Add efficient support for continuous timers as well. - * - * Revision 1.1 2000/10/26 15:49:16 johana - * Added fasttimer, highresolution timers. - * - * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden */ #include <linux/errno.h> @@ -122,9 +21,9 @@ #include <linux/version.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> #include <asm/fasttimer.h> #include <linux/proc_fs.h> @@ -140,30 +39,25 @@ #define DEBUG_LOG_INCLUDED #define FAST_TIMER_LOG -//#define FAST_TIMER_TEST +/* #define FAST_TIMER_TEST */ #define FAST_TIMER_SANITY_CHECKS #ifdef FAST_TIMER_SANITY_CHECKS -#define SANITYCHECK(x) x -static int sanity_failed = 0; -#else -#define SANITYCHECK(x) +static int sanity_failed; #endif #define D1(x) #define D2(x) #define DP(x) -#define __INLINE__ inline - -static int fast_timer_running = 0; -static int fast_timers_added = 0; -static int fast_timers_started = 0; -static int fast_timers_expired = 0; -static int fast_timers_deleted = 0; -static int fast_timer_is_init = 0; -static int fast_timer_ints = 0; +static unsigned int fast_timer_running; +static unsigned int fast_timers_added; +static unsigned int fast_timers_started; +static unsigned int fast_timers_expired; +static unsigned int fast_timers_deleted; +static unsigned int fast_timer_is_init; +static unsigned int fast_timer_ints; struct fast_timer *fast_timer_list = NULL; @@ -171,8 +65,8 @@ struct fast_timer *fast_timer_list = NULL; #define DEBUG_LOG_MAX 128 static const char * debug_log_string[DEBUG_LOG_MAX]; static unsigned long debug_log_value[DEBUG_LOG_MAX]; -static int debug_log_cnt = 0; -static int debug_log_cnt_wrapped = 0; +static unsigned int debug_log_cnt; +static unsigned int debug_log_cnt_wrapped; #define DEBUG_LOG(string, value) \ { \ @@ -202,103 +96,92 @@ struct fast_timer timer_expired_log[NUM_TIMER_STATS]; int timer_div_settings[NUM_TIMER_STATS]; int timer_delay_settings[NUM_TIMER_STATS]; +struct work_struct fast_work; static void -timer_trig_handler(void); +timer_trig_handler(struct work_struct *work); /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ -void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +inline void do_gettimeofday_fast(struct fasttime_t *tv) { - unsigned long sec = jiffies; - unsigned long usec = GET_JIFFIES_USEC(); - - usec += (sec % HZ) * (1000000 / HZ); - sec = sec / HZ; - - if (usec > 1000000) - { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_jiff = jiffies; + tv->tv_usec = GET_JIFFIES_USEC(); } -int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1) { - if (t0->tv_sec < t1->tv_sec) - { - return -1; - } - else if (t0->tv_sec > t1->tv_sec) - { - return 1; - } - if (t0->tv_usec < t1->tv_usec) - { - return -1; - } - else if (t0->tv_usec > t1->tv_usec) - { - return 1; - } - return 0; + /* Compare jiffies. Takes care of wrapping */ + if (time_before(t0->tv_jiff, t1->tv_jiff)) + return -1; + else if (time_after(t0->tv_jiff, t1->tv_jiff)) + return 1; + + /* Compare us */ + if (t0->tv_usec < t1->tv_usec) + return -1; + else if (t0->tv_usec > t1->tv_usec) + return 1; + return 0; } /* Called with ints off */ -void __INLINE__ start_timer_trig(unsigned long delay_us) +inline void start_timer_trig(unsigned long delay_us) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; reg_timer_rw_trig trig; reg_timer_rw_trig_cfg trig_cfg = { 0 }; - reg_timer_r_time r_time; + reg_timer_r_time r_time0; + reg_timer_r_time r_time1; + unsigned char trig_wrap; + unsigned char time_wrap; - r_time = REG_RD(timer, regi_timer, r_time); + r_time0 = REG_RD(timer, regi_timer0, r_time); D1(printk("start_timer_trig : %d us freq: %i div: %i\n", delay_us, freq_index, div)); /* Clear trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); - /* Set timer values */ - /* r_time is 100MHz (10 ns resolution) */ - trig = r_time + delay_us*(1000/10); + /* Set timer values and check if trigger wraps. */ + /* r_time is 100MHz (10 ns resolution) */ + trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0; timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig; timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); /* Start timer */ - REG_WR(timer, regi_timer, rw_trig, trig); + REG_WR(timer, regi_timer0, rw_trig, trig); trig_cfg.tmr = regk_timer_time; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Check if we have already passed the trig time */ - r_time = REG_RD(timer, regi_timer, r_time); - if (r_time < trig) { + r_time1 = REG_RD(timer, regi_timer0, r_time); + time_wrap = r_time1 < r_time0; + + if ((trig_wrap && !time_wrap) || (r_time1 < trig)) { /* No, Enable trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 1; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); fast_timers_started++; fast_timer_running = 1; - } - else - { + } else { /* We have passed the time, disable trig point, ack intr */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); - /* call the int routine directly */ - timer_trig_handler(); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); + /* call the int routine */ + INIT_WORK(&fast_work, timer_trig_handler); + schedule_work(&fast_work); } } @@ -320,22 +203,20 @@ void start_one_shot_timer(struct fast_timer *t, do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; - SANITYCHECK({ /* Check so this is not in the list already... */ - while (tmp != NULL) - { - if (tmp == t) - { - printk("timer name: %s data: 0x%08lX already in list!\n", name, data); - sanity_failed++; - return; - } - else - { - tmp = tmp->next; - } - } - tmp = fast_timer_list; - }); +#ifdef FAST_TIMER_SANITY_CHECKS + /* Check so this is not in the list already... */ + while (tmp != NULL) { + if (tmp == t) { + printk(KERN_DEBUG + "timer name: %s data: 0x%08lX already " + "in list!\n", name, data); + sanity_failed++; + goto done; + } else + tmp = tmp->next; + } + tmp = fast_timer_list; +#endif t->delay_us = delay_us; t->function = function; @@ -343,11 +224,10 @@ void start_one_shot_timer(struct fast_timer *t, t->name = name; t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; - t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; - if (t->tv_expires.tv_usec > 1000000) - { + t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ; + if (t->tv_expires.tv_usec > 1000000) { t->tv_expires.tv_usec -= 1000000; - t->tv_expires.tv_sec++; + t->tv_expires.tv_jiff += HZ; } #ifdef FAST_TIMER_LOG timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; @@ -355,15 +235,12 @@ void start_one_shot_timer(struct fast_timer *t, fast_timers_added++; /* Check if this should timeout before anything else */ - if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) - { + if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0) { /* Put first in list and modify the timer value */ t->prev = NULL; t->next = fast_timer_list; if (fast_timer_list) - { fast_timer_list->prev = t; - } fast_timer_list = t; #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; @@ -372,10 +249,8 @@ void start_one_shot_timer(struct fast_timer *t, } else { /* Put in correct place in list */ while (tmp->next && - timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) - { + fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) tmp = tmp->next; - } /* Insert t after tmp */ t->prev = tmp; t->next = tmp->next; @@ -388,6 +263,7 @@ void start_one_shot_timer(struct fast_timer *t, D2(printk("start_one_shot_timer: %d us done\n", delay_us)); +done: local_irq_restore(flags); } /* start_one_shot_timer */ @@ -431,19 +307,18 @@ int del_fast_timer(struct fast_timer * t) /* Timer interrupt handler for trig interrupts */ static irqreturn_t -timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_trig_interrupt(int irq, void *dev_id) { reg_timer_r_masked_intr masked_intr; - /* Check if the timer interrupt is for us (a trig int) */ - masked_intr = REG_RD(timer, regi_timer, r_masked_intr); + masked_intr = REG_RD(timer, regi_timer0, r_masked_intr); if (!masked_intr.trig) return IRQ_NONE; - timer_trig_handler(); + timer_trig_handler(NULL); return IRQ_HANDLED; } -static void timer_trig_handler(void) +static void timer_trig_handler(struct work_struct *work) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; @@ -451,38 +326,45 @@ static void timer_trig_handler(void) struct fast_timer *t; unsigned long flags; + /* We keep interrupts disabled not only when we modify the + * fast timer list, but any time we hold a reference to a + * timer in the list, since del_fast_timer may be called + * from (another) interrupt context. Thus, the only time + * when interrupts are enabled is when calling the timer + * callback function. + */ local_irq_save(flags); /* Clear timer trig interrupt */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); /* First stop timer, then ack interrupt */ /* Stop timer */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); fast_timer_running = 0; fast_timer_ints++; - local_irq_restore(flags); + fast_timer_function_type *f; + unsigned long d; t = fast_timer_list; - while (t) - { - struct timeval tv; + while (t) { + struct fasttime_t tv; /* Has it really expired? */ do_gettimeofday_fast(&tv); - D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + D1(printk(KERN_DEBUG + "t: %is %06ius\n", tv.tv_jiff, tv.tv_usec)); - if (timeval_cmp(&t->tv_expires, &tv) <= 0) - { + if (fasttime_cmp(&t->tv_expires, &tv) <= 0) { /* Yes it has expired */ #ifdef FAST_TIMER_LOG timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; @@ -490,84 +372,77 @@ static void timer_trig_handler(void) fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - local_irq_save(flags); if (t->prev) - { t->prev->next = t->next; - } else - { fast_timer_list = t->next; - } if (t->next) - { t->next->prev = t->prev; - } t->prev = NULL; t->next = NULL; - local_irq_restore(flags); - if (t->function != NULL) - { - t->function(t->data); - } - else - { + /* Save function callback data before enabling + * interrupts, since the timer may be removed and we + * don't know how it was allocated (e.g. ->function + * and ->data may become overwritten after deletion + * if the timer was stack-allocated). + */ + f = t->function; + d = t->data; + + if (f != NULL) { + /* Run the callback function with interrupts + * enabled. */ + local_irq_restore(flags); + f(d); + local_irq_save(flags); + } else DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints); - } - } - else - { + } else { /* Timer is to early, let's set it again using the normal routines */ D1(printk(".\n")); } - local_irq_save(flags); - if ((t = fast_timer_list) != NULL) - { + t = fast_timer_list; + if (t != NULL) { /* Start next timer.. */ - long us; - struct timeval tv; + long us = 0; + struct fasttime_t tv; do_gettimeofday_fast(&tv); - us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + - t->tv_expires.tv_usec - tv.tv_usec); - if (us > 0) - { - if (!fast_timer_running) - { + + /* time_after_eq takes care of wrapping */ + if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff)) + us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * + 1000000 / HZ + t->tv_expires.tv_usec - + tv.tv_usec); + + if (us > 0) { + if (!fast_timer_running) { #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; #endif start_timer_trig(us); } - local_irq_restore(flags); break; - } - else - { + } else { /* Timer already expired, let's handle it better late than never. * The normal loop handles it */ D1(printk("e! %d\n", us)); } } - local_irq_restore(flags); } - if (!t) - { + local_irq_restore(flags); + + if (!t) D1(printk("ttrig stop!\n")); - } } static void wake_up_func(unsigned long data) { -#ifdef DECLARE_WAITQUEUE wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; -#else - struct wait_queue **sleep_wait_p = (struct wait_queue **)data; -#endif wake_up(sleep_wait_p); } @@ -577,28 +452,17 @@ static void wake_up_func(unsigned long data) void schedule_usleep(unsigned long us) { struct fast_timer t; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t sleep_wait; init_waitqueue_head(&sleep_wait); - { - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue *sleep_wait = NULL; - struct wait_queue wait = { current, NULL }; -#endif D1(printk("schedule_usleep(%d)\n", us)); - add_wait_queue(&sleep_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, "usleep"); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&sleep_wait, &wait); + /* Uninterruptible sleep on the fast timer. (The condition is + * somewhat redundant since the timer is what wakes us up.) */ + wait_event(sleep_wait, !fast_timer_pending(&t)); + D1(printk("done schedule_usleep(%d)\n", us)); -#ifdef DECLARE_WAITQUEUE - } -#endif } #ifdef CONFIG_PROC_FS @@ -618,20 +482,22 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len unsigned long flags; int i = 0; int num_to_show; - struct timeval tv; + struct fasttime_t tv; struct fast_timer *t, *nextt; static char *bigbuf = NULL; static unsigned long used; - if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) - { - used = 0; - bigbuf[0] = '\0'; - return 0; - } - - if (!offset || !used) - { + if (!bigbuf) { + bigbuf = vmalloc(BIG_BUF_SIZE); + if (!bigbuf) { + used = 0; + if (buf) + buf[0] = '\0'; + return 0; + } + } + + if (!offset || !used) { do_gettimeofday_fast(&tv); used = 0; @@ -648,7 +514,7 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Fast timer running: %s\n", fast_timer_running ? "yes" : "no"); used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", - (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_jiff, (unsigned long)tv.tv_usec); #ifdef FAST_TIMER_SANITY_CHECKS used += sprintf(bigbuf + used, "Sanity failed: %i\n", @@ -661,10 +527,8 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len int end_i = debug_log_cnt; i = 0; - if (debug_log_cnt_wrapped) - { + if (debug_log_cnt_wrapped) i = debug_log_cnt; - } while ((i != end_i || (debug_log_cnt_wrapped && !used)) && used+100 < BIG_BUF_SIZE) @@ -697,9 +561,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -719,9 +583,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -739,9 +603,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -752,26 +616,25 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Active timers:\n"); local_irq_save(flags); - local_irq_save(flags); t = fast_timer_list; while (t != NULL && (used+100 < BIG_BUF_SIZE)) { nextt = t->next; local_irq_restore(flags); used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " - "d: %6li us data: 0x%08lX" -/* " func: 0x%08lX" */ - "\n", - t->name, - (unsigned long)t->tv_set.tv_sec, - (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, - (unsigned long)t->tv_expires.tv_usec, + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_jiff, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_jiff, + (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data /* , t->function */ ); - local_irq_disable(); + local_irq_save(flags); if (t->next != nextt) { printk("timer removed!\n"); @@ -800,7 +663,7 @@ static volatile int num_test_timeout = 0; static struct fast_timer tr[10]; static int exp_num[10]; -static struct timeval tv_exp[100]; +static struct fasttime_t tv_exp[100]; static void test_timeout(unsigned long data) { @@ -838,7 +701,7 @@ static void fast_timer_test(void) int prev_num; int j; - struct timeval tv, tv0, tv1, tv2; + struct fasttime_t tv, tv0, tv1, tv2; printk("fast_timer_test() start\n"); do_gettimeofday_fast(&tv); @@ -851,21 +714,22 @@ static void fast_timer_test(void) { do_gettimeofday_fast(&tv_exp[j]); } - printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec); for (j = 0; j < 1000; j++) { - printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + printk(KERN_DEBUG "%i %i %i %i %i\n", + j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); j += 4; } for (j = 0; j < 100; j++) { - printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", - tv_exp[j].tv_sec,tv_exp[j].tv_usec, - tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, - tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, - tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, - tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, + tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec); j += 4; } do_gettimeofday_fast(&tv0); @@ -892,14 +756,15 @@ static void fast_timer_test(void) while (num_test_timeout < i) { if (num_test_timeout != prev_num) - { prev_num = num_test_timeout; - } } do_gettimeofday_fast(&tv2); - printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); - printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); - printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + printk(KERN_INFO "Timers started %is %06i\n", + tv0.tv_jiff, tv0.tv_usec); + printk(KERN_INFO "Timers started at %is %06i\n", + tv1.tv_jiff, tv1.tv_usec); + printk(KERN_INFO "Timers done %is %06i\n", + tv2.tv_jiff, tv2.tv_usec); DP(printk("buf0:\n"); printk(buf0); printk("buf1:\n"); @@ -921,9 +786,9 @@ static void fast_timer_test(void) printk("%-10s set: %6is %06ius exp: %6is %06ius " "data: 0x%08X func: 0x%08X\n", t->name, - t->tv_set.tv_sec, + t->tv_set.tv_jiff, t->tv_set.tv_usec, - t->tv_expires.tv_sec, + t->tv_expires.tv_jiff, t->tv_expires.tv_usec, t->data, t->function @@ -931,10 +796,12 @@ static void fast_timer_test(void) printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", t->delay_us, - tv_exp[j].tv_sec, + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, exp_num[j], - (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) * + 1000000 + tv_exp[j].tv_usec - + t->tv_expires.tv_usec); } proc_fasttimer_read(buf5, NULL, 0, 0, 0); printk("buf5 after all done:\n"); @@ -944,7 +811,7 @@ static void fast_timer_test(void) #endif -void fast_timer_init(void) +int fast_timer_init(void) { /* For some reason, request_irq() hangs when called froom time_init() */ if (!fast_timer_is_init) @@ -952,18 +819,20 @@ void fast_timer_init(void) printk("fast_timer_init()\n"); #ifdef CONFIG_PROC_FS - if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) - fasttimer_proc_entry->read_proc = proc_fasttimer_read; + fasttimer_proc_entry = create_proc_entry("fasttimer", 0, 0); + if (fasttimer_proc_entry) + fasttimer_proc_entry->read_proc = proc_fasttimer_read; #endif /* PROC_FS */ - if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, IRQF_DISABLED, - "fast timer int", NULL)) - { - printk("err: timer1 irq\n"); - } + if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "fast timer int", &fast_timer_list)) + printk(KERN_ERR "err: fasttimer irq\n"); fast_timer_is_init = 1; #ifdef FAST_TIMER_TEST printk("do test\n"); fast_timer_test(); #endif } + return 0; } +__initcall(fast_timer_init); diff --git a/arch/cris/arch-v32/kernel/head.S b/arch/cris/arch-v32/kernel/head.S index 20bd80a84e4..2d66a7c320e 100644 --- a/arch/cris/arch-v32/kernel/head.S +++ b/arch/cris/arch-v32/kernel/head.S @@ -4,22 +4,25 @@ * Copyright (C) 2003, Axis Communications AB */ - #define ASSEMBLER_MACROS_ONLY /* * The macros found in mmu_defs_asm.h uses the ## concatenation operator, so * -traditional must not be used when assembling this file. */ -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/asm/mmu_defs_asm.h> -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/config_defs_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/reg_rdwr.h> +#include <asm/arch/memmap.h> +#include <hwregs/intr_vect.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define CRAMFS_MAGIC 0x28cd3d45 +#define JHEAD_MAGIC 0x1FF528A6 +#define JHEAD_SIZE 8 #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 +#define NAND_BOOT_MAGIC 0x9a9db001 ;; NOTE: R8 and R9 carry information from the decompressor (if the ;; kernel was compressed). They must not be used in the code below @@ -30,12 +33,11 @@ .global romfs_start .global romfs_length .global romfs_in_flash + .global nand_boot .global swapper_pg_dir - .global crisv32_nand_boot - .global crisv32_nand_cramfs_offset ;; Dummy section to make it bootable with current VCS simulator -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM .section ".boot", "ax" ba tstart nop @@ -51,33 +53,15 @@ tstart: ;; di - ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] - - ;; Set up waitstates etc - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP1_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP2_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1 - move.d $r1, [$r0] - -#ifdef CONFIG_ETRAXFS_SIM - ;; Set up minimal flash waitstates - move.d 0, $r10 - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11 - move.d $r10, [$r11] + START_CLOCKS + + SETUP_WAIT_STATES + + GIO_INIT + +#ifdef CONFIG_SMP +secondary_cpu_entry: /* Entry point for secondary CPUs */ + di #endif ;; Setup and enable the MMU. Use same configuration for both the data @@ -85,7 +69,7 @@ tstart: ;; ;; Note; 3 cycles is needed for a bank-select to take effect. Further; ;; bank 1 is the instruction MMU, bank 2 is the data MMU. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0 @@ -93,7 +77,7 @@ tstart: ;; Map the virtual DRAM to the RW eprom area at address 0. ;; Also map 0xa for the hook calls, move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ - | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0 #endif @@ -104,7 +88,7 @@ tstart: ;; Enable certain page protections and setup linear mapping ;; for f,e,c,b,4,0. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_STATE(mmu, rw_mm_cfg, we, on) \ | REG_STATE(mmu, rw_mm_cfg, acc, on) \ | REG_STATE(mmu, rw_mm_cfg, ex, on) \ @@ -183,17 +167,11 @@ tstart: nop nop nop - move $s10, $r0 + move $s12, $r0 cmpq 0, $r0 beq master_cpu nop slave_cpu: - ; A slave waits for cpu_now_booting to be equal to CPU ID. - move.d cpu_now_booting, $r1 -slave_wait: - cmp.d [$r1], $r0 - bne slave_wait - nop ; Time to boot-up. Get stack location provided by master CPU. move.d smp_init_current_idle_thread, $r1 move.d [$r1], $sp @@ -203,9 +181,16 @@ slave_wait: jsr smp_callin nop master_cpu: + /* Set up entry point for secondary CPUs. The boot ROM has set up + * EBP at start of internal memory. The CPU will get there + * later when we issue an IPI to them... */ + move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0 + move.d secondary_cpu_entry, $r1 + move.d $r1, [$r0] #endif -#ifndef CONFIG_ETRAXFS_SIM - ;; Check if starting from DRAM or flash. +#ifndef CONFIG_ETRAX_VCS_SIM + ; Check if starting from DRAM (network->RAM boot or unpacked + ; compressed kernel), or directly from flash. lapcq ., $r0 and.d 0x7fffffff, $r0 ; Mask off the non-cache bit. cmp.d 0x10000, $r0 ; Arbitrary, something above this code. @@ -232,12 +217,13 @@ _inflash: beq _dram_initialized nop -#include "../lib/dram_init.S" +#include "../mach/dram_init.S" _dram_initialized: ;; Copy the text and data section to DRAM. This depends on that the ;; variables used below are correctly set up by the linker script. ;; The calculated value stored in R4 is used below. + ;; Leave the cramfs file system (piggybacked after the kernel) in flash. moveq 0, $r0 ; Source. move.d text_start, $r1 ; Destination. move.d __vmlinux_end, $r2 @@ -249,7 +235,7 @@ _dram_initialized: blo 1b nop - ;; Keep CRAMFS in flash. + ;; Check for cramfs. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] @@ -258,6 +244,7 @@ _dram_initialized: bne 1f nop + ;; Set length and start of cramfs, set romfs_in_flash flag addoq +4, $r4, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -273,35 +260,32 @@ _dram_initialized: nop _inram: - ;; Check if booting from NAND flash (in that case we just remember the offset - ;; into the flash where cramfs should be). - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne move_cramfs - moveq 1,$r0 - move.d crisv32_nand_boot, $r1 - move.d $r0, [$r1] - move.d crisv32_nand_cramfs_offset, $r1 - move.d $r9, [$r1] + ;; Check if booting from NAND flash; if so, set appropriate flags + ;; and move on. + cmp.d NAND_BOOT_MAGIC, $r12 + bne move_cramfs ; not nand, jump moveq 1, $r0 - move.d romfs_in_flash, $r1 + move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND + move.d $r0, [$r1] + moveq 0, $r0 ; tell axisflashmap romfs is not in + move.d romfs_in_flash, $r1 ; (directly accessed) flash move.d $r0, [$r1] - jump _start_it + jump _start_it ; continue with boot nop move_cramfs: - ;; Move the cramfs after BSS. + ;; kernel is in DRAM. + ;; Must figure out if there is a piggybacked rootfs image or not. + ;; Set romfs_length to 0 => no rootfs image available by default. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; The kernel could have been unpacked to DRAM by the loader, but - ;; the cramfs image could still be inte the flash immediately - ;; following the compressed kernel image. The loaded passes the address - ;; of the bute succeeding the last compressed byte in the flash in + ;; the cramfs image could still be in the flash immediately + ;; following the compressed kernel image. The loader passes the address + ;; of the byte succeeding the last compressed byte in the flash in ;; register R9 when starting the kernel. cmp.d 0x0ffffff8, $r9 bhs _no_romfs_in_flash ; R9 points outside the flash area. @@ -310,11 +294,13 @@ move_cramfs: ba _no_romfs_in_flash nop #endif + ;; cramfs rootfs might to be in flash. Check for it. move.d [$r9], $r0 ; cramfs_super.magic cmp.d CRAMFS_MAGIC, $r0 bne _no_romfs_in_flash nop + ;; found cramfs in flash. set address and size, and romfs_in_flash flag. addoq +4, $r9, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -330,27 +316,43 @@ move_cramfs: nop _no_romfs_in_flash: - ;; Look for cramfs. + ;; No romfs in flash, so look for cramfs, or jffs2 with jhead, + ;; after kernel in RAM, as is the case with network->RAM boot. + ;; For cramfs, partition starts with magic and length. + ;; For jffs2, a jhead is prepended which contains with magic and length. + ;; The jhead is not part of the jffs2 partition however. #ifndef CONFIG_ETRAXFS_SIM move.d __vmlinux_end, $r0 #else move.d __end, $r0 #endif move.d [$r0], $r1 - cmp.d CRAMFS_MAGIC, $r1 - bne 2f + cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic? + beq 2f ; yes, jump + nop + cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic? + bne 4f ; no, skip copy nop + addq 4, $r0 ; location of jffs2 size + move.d [$r0+], $r2 ; fetch jffs2 size -> r2 + ; r0 now points to start of jffs2 + ba 3f + nop +2: + addoq +4, $r0, $acr ; location of cramfs size + move.d [$acr], $r2 ; fetch cramfs size -> r2 + ; r0 still points to start of cramfs +3: + ;; Now, move the root fs to after kernel's BSS - addoq +4, $r0, $acr - move.d [$acr], $r2 - move.d _end, $r1 + move.d _end, $r1 ; start of cramfs -> r1 move.d romfs_start, $r3 - move.d $r1, [$r3] + move.d $r1, [$r3] ; store at romfs_start (for axisflashmap) move.d romfs_length, $r3 - move.d $r2, [$r3] + move.d $r2, [$r3] ; store size at romfs_length -#ifndef CONFIG_ETRAXFS_SIM - add.d $r2, $r0 +#ifndef CONFIG_ETRAX_VCS_SIM + add.d $r2, $r0 ; copy from end and downwards add.d $r2, $r1 lsrq 1, $r2 ; Size is in bytes, we copy words. @@ -365,10 +367,17 @@ _no_romfs_in_flash: nop #endif -2: +4: + ;; BSS move done. + ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM + ;; Also clear nand_boot flag; if we got here, we know we've not + ;; booted from NAND flash. moveq 0, $r0 move.d romfs_in_flash, $r1 move.d $r0, [$r1] + moveq 0, $r0 + move.d nand_boot, $r1 + move.d $r0, [$r1] jump _start_it ; Jump to cached code. nop @@ -384,8 +393,8 @@ _start_it: move.d cris_command_line, $r10 or.d 0x80000000, $r11 ; Make it virtual 1: - move.b [$r11+], $r12 - move.b $r12, [$r10+] + move.b [$r11+], $r1 + move.b $r1, [$r10+] subq 1, $r13 bne 1b nop @@ -401,7 +410,7 @@ no_command_line: move.d etrax_irv, $r1 ; Set the exception base register and pointer. move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; Clear the BSS region from _bss_start to _end. move.d __bss_start, $r0 move.d _end, $r1 @@ -411,7 +420,7 @@ no_command_line: nop #endif -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM /* Set the watchdog timeout to something big. Will be removed when */ /* watchdog can be disabled with command line option */ move.d 0x7fffffff, $r10 @@ -423,25 +432,44 @@ no_command_line: move.d __bss_start, $r0 movem [$r0], $r13 +#ifdef CONFIG_ETRAX_L2CACHE + jsr l2cache_init + nop +#endif + jump start_kernel ; Jump to start_kernel() in init/main.c. nop .data etrax_irv: .dword 0 + +; Variables for communication with the Axis flash map driver (axisflashmap), +; and for setting up memory in arch/cris/kernel/setup.c . + +; romfs_start is set to the start of the root file system, if it exists +; in directly accessible memory (i.e. NOR Flash when booting from Flash, +; or RAM when booting directly from a network-downloaded RAM image) romfs_start: .dword 0 + +; romfs_length is set to the size of the root file system image, if it exists +; in directly accessible memory (see romfs_start). Otherwise it is set to 0. romfs_length: .dword 0 + +; romfs_in_flash is set to 1 if the root file system resides in directly +; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot +; or NAND flash boot. romfs_in_flash: .dword 0 -crisv32_nand_boot: - .dword 0 -crisv32_nand_cramfs_offset: + +; nand_boot is set to 1 when the kernel has been booted from NAND flash +nand_boot: .dword 0 swapper_pg_dir = 0xc0002000 .section ".init.data", "aw" -#include "../lib/hw_settings.S" +#include "../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/kernel/io.c b/arch/cris/arch-v32/kernel/io.c deleted file mode 100644 index a22a9e02e09..00000000000 --- a/arch/cris/arch-v32/kernel/io.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Helper functions for I/O pins. - * - * Copyright (c) 2004 Axis Communications AB. - */ - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/string.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/io.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/gio_defs.h> - -struct crisv32_ioport crisv32_ioports[] = -{ - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pa_din), - 8 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pb_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pc_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pd_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din), - 18 - } -}; - -#define NBR_OF_PORTS ARRAY_SIZE(crisv32_ioports) - -struct crisv32_iopin crisv32_led1_green; -struct crisv32_iopin crisv32_led1_red; -struct crisv32_iopin crisv32_led2_green; -struct crisv32_iopin crisv32_led2_red; -struct crisv32_iopin crisv32_led3_green; -struct crisv32_iopin crisv32_led3_red; - -/* Dummy port used when green LED and red LED is on the same bit */ -static unsigned long io_dummy; -static struct crisv32_ioport dummy_port = -{ - &io_dummy, - &io_dummy, - &io_dummy, - 18 -}; -static struct crisv32_iopin dummy_led = -{ - &dummy_port, - 0 -}; - -static int __init crisv32_io_init(void) -{ - int ret = 0; - /* Initialize LEDs */ - ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G); - ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R); - ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G); - ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R); - ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G); - ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R); - crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); - - if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R)) - crisv32_led1_red = dummy_led; - if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R)) - crisv32_led2_red = dummy_led; - - return ret; -} - -__initcall(crisv32_io_init); - -int crisv32_io_get(struct crisv32_iopin* iopin, - unsigned int port, unsigned int pin) -{ - if (port > NBR_OF_PORTS) - return -EINVAL; - if (port > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -int crisv32_io_get_name(struct crisv32_iopin* iopin, - char* name) -{ - int port; - int pin; - - if (toupper(*name) == 'P') - name++; - - if (toupper(*name) < 'A' || toupper(*name) > 'E') - return -EINVAL; - - port = toupper(*name) - 'A'; - name++; - pin = simple_strtoul(name, NULL, 10); - - if (pin < 0 || pin > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -#ifdef CONFIG_PCI -/* PCI I/O access stuff */ -struct cris_io_operations* cris_iops = NULL; -EXPORT_SYMBOL(cris_iops); -#endif - diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c index a9acaa27024..173c141ac9b 100644 --- a/arch/cris/arch-v32/kernel/irq.c +++ b/arch/cris/arch-v32/kernel/irq.c @@ -15,15 +15,21 @@ #include <linux/threads.h> #include <linux/spinlock.h> #include <linux/kernel_stat.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #define CPU_FIXED -1 /* IRQ masks (refer to comment for crisv32_do_multiple) */ -#define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ)) +#if TIMER0_INTR_VECT - FIRST_IRQ < 32 +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ)) +#undef TIMER_VECT1 +#else +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32)) +#define TIMER_VECT1 +#endif #ifdef CONFIG_ETRAX_KGDB #if defined(CONFIG_ETRAX_KGDB_PORT0) #define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ)) @@ -44,8 +50,8 @@ struct cris_irq_allocation cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ }; -struct cris_irq_allocation irq_allocations[NR_IRQS] = - {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; +struct cris_irq_allocation irq_allocations[NR_REAL_IRQS] = + { [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} }; static unsigned long irq_regs[NR_CPUS] = { @@ -55,6 +61,12 @@ static unsigned long irq_regs[NR_CPUS] = #endif }; +#if NR_REAL_IRQS > 32 +#define NBR_REGS 2 +#else +#define NBR_REGS 1 +#endif + unsigned long cpu_irq_counters[NR_CPUS]; unsigned long irq_counters[NR_REAL_IRQS]; @@ -79,45 +91,81 @@ extern void d_mmu_write(void); extern void kgdb_init(void); extern void breakpoint(void); +/* From traps.c. */ +extern void breakh_BUG(void); + /* - * Build the IRQ handler stubs using macros from irq.h. First argument is the - * IRQ number, the second argument is the corresponding bit in - * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. + * Build the IRQ handler stubs using macros from irq.h. */ -BUILD_IRQ(0x31, (1 << 0)) /* memarb */ -BUILD_IRQ(0x32, (1 << 1)) /* gen_io */ -BUILD_IRQ(0x33, (1 << 2)) /* iop0 */ -BUILD_IRQ(0x34, (1 << 3)) /* iop1 */ -BUILD_IRQ(0x35, (1 << 4)) /* iop2 */ -BUILD_IRQ(0x36, (1 << 5)) /* iop3 */ -BUILD_IRQ(0x37, (1 << 6)) /* dma0 */ -BUILD_IRQ(0x38, (1 << 7)) /* dma1 */ -BUILD_IRQ(0x39, (1 << 8)) /* dma2 */ -BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */ -BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */ -BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */ -BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */ -BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */ -BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */ -BUILD_IRQ(0x40, (1 << 15)) /* dma9 */ -BUILD_IRQ(0x41, (1 << 16)) /* ata */ -BUILD_IRQ(0x42, (1 << 17)) /* sser0 */ -BUILD_IRQ(0x43, (1 << 18)) /* sser1 */ -BUILD_IRQ(0x44, (1 << 19)) /* ser0 */ -BUILD_IRQ(0x45, (1 << 20)) /* ser1 */ -BUILD_IRQ(0x46, (1 << 21)) /* ser2 */ -BUILD_IRQ(0x47, (1 << 22)) /* ser3 */ -BUILD_IRQ(0x48, (1 << 23)) -BUILD_IRQ(0x49, (1 << 24)) /* eth0 */ -BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */ -BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */ -BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */ -BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */ -BUILD_IRQ(0x4e, (1 << 29)) /* ext */ -BUILD_IRQ(0x4f, (1 << 29)) /* ipi */ +BUILD_IRQ(0x31) +BUILD_IRQ(0x32) +BUILD_IRQ(0x33) +BUILD_IRQ(0x34) +BUILD_IRQ(0x35) +BUILD_IRQ(0x36) +BUILD_IRQ(0x37) +BUILD_IRQ(0x38) +BUILD_IRQ(0x39) +BUILD_IRQ(0x3a) +BUILD_IRQ(0x3b) +BUILD_IRQ(0x3c) +BUILD_IRQ(0x3d) +BUILD_IRQ(0x3e) +BUILD_IRQ(0x3f) +BUILD_IRQ(0x40) +BUILD_IRQ(0x41) +BUILD_IRQ(0x42) +BUILD_IRQ(0x43) +BUILD_IRQ(0x44) +BUILD_IRQ(0x45) +BUILD_IRQ(0x46) +BUILD_IRQ(0x47) +BUILD_IRQ(0x48) +BUILD_IRQ(0x49) +BUILD_IRQ(0x4a) +BUILD_IRQ(0x4b) +BUILD_IRQ(0x4c) +BUILD_IRQ(0x4d) +BUILD_IRQ(0x4e) +BUILD_IRQ(0x4f) +BUILD_IRQ(0x50) +#if MACH_IRQS > 32 +BUILD_IRQ(0x51) +BUILD_IRQ(0x52) +BUILD_IRQ(0x53) +BUILD_IRQ(0x54) +BUILD_IRQ(0x55) +BUILD_IRQ(0x56) +BUILD_IRQ(0x57) +BUILD_IRQ(0x58) +BUILD_IRQ(0x59) +BUILD_IRQ(0x5a) +BUILD_IRQ(0x5b) +BUILD_IRQ(0x5c) +BUILD_IRQ(0x5d) +BUILD_IRQ(0x5e) +BUILD_IRQ(0x5f) +BUILD_IRQ(0x60) +BUILD_IRQ(0x61) +BUILD_IRQ(0x62) +BUILD_IRQ(0x63) +BUILD_IRQ(0x64) +BUILD_IRQ(0x65) +BUILD_IRQ(0x66) +BUILD_IRQ(0x67) +BUILD_IRQ(0x68) +BUILD_IRQ(0x69) +BUILD_IRQ(0x6a) +BUILD_IRQ(0x6b) +BUILD_IRQ(0x6c) +BUILD_IRQ(0x6d) +BUILD_IRQ(0x6e) +BUILD_IRQ(0x6f) +BUILD_IRQ(0x70) +#endif /* Pointers to the low-level handlers. */ -static void (*interrupt[NR_IRQS])(void) = { +static void (*interrupt[MACH_IRQS])(void) = { IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt, IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt, IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt, @@ -128,7 +176,20 @@ static void (*interrupt[NR_IRQS])(void) = { IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt, IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt, IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt, - IRQ0x4f_interrupt + IRQ0x4f_interrupt, IRQ0x50_interrupt, +#if MACH_IRQS > 32 + IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt, + IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt, + IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt, + IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt, + IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt, + IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt, + IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt, + IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt, + IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt, + IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt, + IRQ0x6f_interrupt, IRQ0x70_interrupt, +#endif }; void @@ -137,13 +198,26 @@ block_irq(int irq, int cpu) int intr_mask; unsigned long flags; - spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask &= ~(1 << (irq - FIRST_IRQ)); - - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); + spin_lock_irqsave(&irq_lock, flags); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask &= ~(1 << (irq - FIRST_IRQ)); + else + intr_mask &= ~(1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -154,12 +228,26 @@ unblock_irq(int irq, int cpu) unsigned long flags; spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask |= (1 << (irq - FIRST_IRQ)); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask |= (1 << (irq - FIRST_IRQ)); + else + intr_mask |= (1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -298,8 +386,9 @@ crisv32_do_multiple(struct pt_regs* regs) { int cpu; int mask; - int masked; + int masked[NBR_REGS]; int bit; + int i; cpu = smp_processor_id(); @@ -308,42 +397,59 @@ crisv32_do_multiple(struct pt_regs* regs) */ irq_enter(); - /* Get which IRQs that happened. */ - masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); + for (i = 0; i < NBR_REGS; i++) { + /* Get which IRQs that happend. */ + masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + r_masked_vect, i); - /* Calculate new IRQ mask with these IRQs disabled. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask &= ~masked; + /* Calculate new IRQ mask with these IRQs disabled. */ + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask &= ~masked[i]; /* Timer IRQ is never masked */ - if (masked & TIMER_MASK) - mask |= TIMER_MASK; - - /* Block all the IRQs */ - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#else + if ((i == 0) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#endif + /* Block all the IRQs */ + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); /* Check for timer IRQ and handle it special. */ - if (masked & TIMER_MASK) { - masked &= ~TIMER_MASK; - do_IRQ(TIMER_INTR_VECT, regs); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } +#else + if ((i == 0) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } } +#endif #ifdef IGNORE_MASK /* Remove IRQs that can't be handled as multiple. */ - masked &= ~IGNORE_MASK; + masked[0] &= ~IGNORE_MASK; #endif /* Handle the rest of the IRQs. */ - for (bit = 0; bit < 32; bit++) - { - if (masked & (1 << bit)) - do_IRQ(bit + FIRST_IRQ, regs); + for (i = 0; i < NBR_REGS; i++) { + for (bit = 0; bit < 32; bit++) { + if (masked[i] & (1 << bit)) + do_IRQ(bit + FIRST_IRQ + i*32, regs); + } } /* Unblock all the IRQs. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask |= masked; - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); + for (i = 0; i < NBR_REGS; i++) { + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask |= masked[i]; + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); + } /* This irq_exit() will trigger the soft IRQs. */ irq_exit(); @@ -361,20 +467,21 @@ init_IRQ(void) reg_intr_vect_rw_mask vect_mask = {0}; /* Clear all interrupts masks. */ - REG_WR(intr_vect, regi_irq, rw_mask, vect_mask); + for (i = 0; i < NBR_REGS; i++) + REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask); for (i = 0; i < 256; i++) etrax_irv->v[i] = weird_irq; - /* Point all IRQs to bad handlers. */ + /* Point all IRQ's to bad handlers. */ for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { irq_desc[j].chip = &crisv32_irq_type; set_exception_vector(i, interrupt[j]); } /* Mark Timer and IPI IRQs as CPU local */ - irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; - irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; + irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; + irq_desc[TIMER0_INTR_VECT].status |= IRQ_PER_CPU; irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; @@ -391,6 +498,11 @@ init_IRQ(void) set_exception_vector(0x0a, d_mmu_access); set_exception_vector(0x0b, d_mmu_write); +#ifdef CONFIG_BUG + /* Break 14 handler, used to implement cheap BUG(). */ + set_exception_vector(0x1e, breakh_BUG); +#endif + /* The system-call trap is reached by "break 13". */ set_exception_vector(0x1d, system_call); diff --git a/arch/cris/arch-v32/kernel/kgdb.c b/arch/cris/arch-v32/kernel/kgdb.c index 480e56348be..4e2e2e271ef 100644 --- a/arch/cris/arch-v32/kernel/kgdb.c +++ b/arch/cris/arch-v32/kernel/kgdb.c @@ -381,7 +381,7 @@ static int read_register(char regno, unsigned int *valptr); /* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ int getDebugChar(void); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM int getDebugChar(void) { return socketread(); @@ -391,7 +391,7 @@ int getDebugChar(void) /* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ void putDebugChar(int val); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM void putDebugChar(int val) { socketwrite((char *)&val, 1); @@ -1599,7 +1599,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT1) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1611,7 +1611,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT2) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1623,7 +1623,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT3) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1635,7 +1635,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask); #endif diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c index b72a15580dc..ced5b725d9b 100644 --- a/arch/cris/arch-v32/kernel/process.c +++ b/arch/cris/arch-v32/kernel/process.c @@ -12,17 +12,13 @@ #include <linux/err.h> #include <linux/fs.h> #include <linux/slab.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> extern void stop_watchdog(void); -#ifdef CONFIG_ETRAX_GPIO -extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */ -#endif - extern int cris_hlt_counter; /* We use this if we don't have any better idle routine. */ @@ -82,7 +78,7 @@ hard_reset_now(void) wd_ctrl.cmd = regk_timer_start; arch_enable_nmi(); - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index 2df60529a8a..e27f4670e88 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2003, Axis Communications AB. + * Copyright (C) 2000-2007, Axis Communications AB. */ #include <linux/kernel.h> @@ -149,7 +149,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = generic_ptrace_pokedata(child, addr, data); break; - /* Write the word at location address in the USER area. */ + /* Write the word at location address in the USER area. */ case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) @@ -201,7 +201,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; - /* Make the child exit by sending it a sigkill. */ + /* Make the child exit by sending it a sigkill. */ case PTRACE_KILL: ret = 0; @@ -245,9 +245,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } + /* Get all GP registers from the child. */ case PTRACE_GETREGS: { - int i; + int i; unsigned long tmp; for (i = 0; i <= PT_MAX; i++) { @@ -294,6 +295,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } +out_tsk: return ret; } diff --git a/arch/cris/arch-v32/kernel/signal.c b/arch/cris/arch-v32/kernel/signal.c index 024cc690197..58c1866804e 100644 --- a/arch/cris/arch-v32/kernel/signal.c +++ b/arch/cris/arch-v32/kernel/signal.c @@ -50,7 +50,7 @@ struct rt_signal_frame { unsigned char retcode[8]; /* Trampoline code. */ }; -int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs); +void do_signal(int restart, struct pt_regs *regs); void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs); /* @@ -61,74 +61,16 @@ int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - sigset_t saveset; - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); - - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* - * This point is reached twice: once to call - * the signal handler, then again to return - * from the sigsuspend system call. When - * calling the signal handler, R10 hold the - * signal number as set by do_signal(). The - * sigsuspend call will always return with - * the restored value above; -EINTR. - */ - return regs->r10; - } - } -} - -/* Define some dummy arguments to be able to reach the regs argument. */ -int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, - long mof, long srp, struct pt_regs *regs) -{ - sigset_t saveset; - sigset_t newset; - - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* See comment in function above. */ - return regs->r10; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } int @@ -290,7 +232,7 @@ sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, goto badframe; if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) - goto badframe; + goto badframe; keep_debug_flags(oldccs, oldspc, regs); @@ -347,11 +289,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) /* Grab and setup a signal frame. * * Basically a lot of state-info is stacked, and arranged for the - * user-mode program to return to the kernel using either a trampoline + * user-mode program to return to the kernel using either a trampiline * which performs the syscall sigreturn(), or a provided user-mode * trampoline. */ -static void +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { @@ -417,16 +359,17 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Actually move the USP to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } -static void +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { @@ -503,21 +446,24 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Actually move the usp to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } /* Invoke a singal handler to, well, handle the signal. */ -static inline void +static inline int handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Check if this got called from a system call. */ if (canrestart) { /* If so, check system call restarting. */ @@ -561,19 +507,24 @@ handle_signal(int canrestart, unsigned long sig, /* Set up the stack frame. */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -587,12 +538,13 @@ handle_signal(int canrestart, unsigned long sig, * we can use user_mode(regs) to see if we came directly from kernel or user * mode below. */ -int -do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +void +do_signal(int canrestart, struct pt_regs *regs) { int signr; siginfo_t info; struct k_sigaction ka; + sigset_t *oldset; /* * The common case should go fast, which is why this point is @@ -600,17 +552,28 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) * without doing anything. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Deliver the signal. */ - handle_signal(canrestart, signr, &info, &ka, oldset, regs); - return 1; + /* Whee! Actually deliver the signal. */ + if (handle_signal(canrestart, signr, &info, &ka, + oldset, regs)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; } /* Got here from a system call? */ @@ -628,7 +591,12 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) } } - return 0; + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } asmlinkage void @@ -641,7 +609,7 @@ ugdb_trap_user(struct thread_info *ti, int sig) user_regs(ti)->spc = 0; } /* FIXME: Filter out false h/w breakpoint hits (i.e. EDA - not within any configured h/w breakpoint range). Synchronize with + not withing any configured h/w breakpoint range). Synchronize with what already exists for kernel debugging. */ if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) { /* Break 8: subtract 2 from ERP unless in a delay slot. */ diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index 171c96e0a5d..a9c3334e46c 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -1,11 +1,12 @@ +#include <linux/types.h> #include <asm/delay.h> -#include <asm/arch/irq.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <irq.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #include <asm/tlbflush.h> #include <asm/mmu_context.h> -#include <asm/arch/hwregs/mmu_defs_asm.h> -#include <asm/arch/hwregs/supp_reg.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/supp_reg.h> #include <asm/atomic.h> #include <linux/err.h> @@ -20,6 +21,7 @@ #define IPI_SCHEDULE 1 #define IPI_CALL 2 #define IPI_FLUSH_TLB 4 +#define IPI_BOOT 8 #define FLUSH_ALL (void*)0xffffffff @@ -30,6 +32,8 @@ spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED}; cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); cpumask_t phys_cpu_present_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map; +EXPORT_SYMBOL(cpu_possible_map); EXPORT_SYMBOL(phys_cpu_present_map); /* Variables used during SMP boot */ @@ -55,13 +59,12 @@ static unsigned long flush_addr; extern int setup_irq(int, struct irqaction *); /* Mode registers */ -static unsigned long irq_regs[NR_CPUS] = -{ +static unsigned long irq_regs[NR_CPUS] = { regi_irq, regi_irq2 }; -static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id); static int send_ipi(int vector, int wait, cpumask_t cpu_mask); static struct irqaction irq_ipi = { .handler = crisv32_ipi_interrupt, @@ -101,6 +104,7 @@ void __devinit smp_prepare_boot_cpu(void) cpu_set(0, cpu_online_map); cpu_set(0, phys_cpu_present_map); + cpu_set(0, cpu_possible_map); } void __init smp_cpus_done(unsigned int max_cpus) @@ -113,6 +117,7 @@ smp_boot_one_cpu(int cpuid) { unsigned timeout; struct task_struct *idle; + cpumask_t cpu_mask = CPU_MASK_NONE; idle = fork_idle(cpuid); if (IS_ERR(idle)) @@ -124,6 +129,12 @@ smp_boot_one_cpu(int cpuid) smp_init_current_idle_thread = task_thread_info(idle); cpu_now_booting = cpuid; + /* Kick it */ + cpu_set(cpuid, cpu_online_map); + cpu_set(cpuid, cpu_mask); + send_ipi(IPI_BOOT, 0, cpu_mask); + cpu_clear(cpuid, cpu_online_map); + /* Wait for CPU to come online */ for (timeout = 0; timeout < 10000; timeout++) { if(cpu_online(cpuid)) { @@ -165,7 +176,7 @@ void __init smp_callin(void) /* Enable IRQ and idle */ REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask); unmask_irq(IPI_INTR_VECT); - unmask_irq(TIMER_INTR_VECT); + unmask_irq(TIMER0_INTR_VECT); preempt_disable(); local_irq_enable(); @@ -328,7 +339,7 @@ int smp_call_function(void (*func)(void *info), void *info, return ret; } -irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id) { void (*func) (void *info) = call_data->func; void *info = call_data->info; diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 2f7e8e200f2..3a13dd6e0a9 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c @@ -1,8 +1,7 @@ -/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $ - * +/* * linux/arch/cris/arch-v32/kernel/time.c * - * Copyright (C) 2003 Axis Communications AB + * Copyright (C) 2003-2007 Axis Communications AB * */ @@ -14,28 +13,34 @@ #include <linux/sched.h> #include <linux/init.h> #include <linux/threads.h> +#include <linux/cpufreq.h> #include <asm/types.h> #include <asm/signal.h> #include <asm/io.h> #include <asm/delay.h> #include <asm/rtc.h> #include <asm/irq.h> - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <asm/irq_regs.h> + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* Watchdog defines */ -#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ -#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ -#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ +#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ +/* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) unsigned long timer_regs[NR_CPUS] = { - regi_timer, + regi_timer0, #ifdef CONFIG_SMP - regi_timer2 + regi_timer2 #endif }; @@ -44,12 +49,22 @@ extern int set_rtc_mmss(unsigned long nowtime); extern int setup_irq(int, struct irqaction *); extern int have_rtc; +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_time_freq_notifier_block = { + .notifier_call = cris_time_freq_notifier, +}; +#endif + unsigned long get_ns_in_jiffie(void) { reg_timer_r_tmr0_data data; unsigned long ns; - data = REG_RD(timer, regi_timer, r_tmr0_data); + data = REG_RD(timer, regi_timer0, r_tmr0_data); ns = (TIMER0_DIV - data) * 10; return ns; } @@ -59,31 +74,27 @@ unsigned long do_slow_gettimeoffset(void) unsigned long count; unsigned long usec_count = 0; - static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + /* For the first call after boot */ + static unsigned long count_p = TIMER0_DIV; static unsigned long jiffies_p = 0; - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ + /* Cache volatile jiffies temporarily; we have IRQs turned off. */ unsigned long jiffies_t; /* The timer interrupt comes from Etrax timer 0. In order to get * better precision, we check the current value. It might have - * underflowed already though. - */ + * underflowed already though. */ + count = REG_RD(timer, regi_timer0, r_tmr0_data); + jiffies_t = jiffies; - count = REG_RD(timer, regi_timer, r_tmr0_data); - jiffies_t = jiffies; - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are one problem that must be avoided here: - * 1. the timer counter underflows + /* Avoiding timer inconsistencies (they are rare, but they happen) + * There is one problem that must be avoided here: + * 1. the timer counter underflows */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { - /* Timer wrapped, use new count and prescale - * increase the time corresponding to one jiffie + /* Timer wrapped, use new count and prescale. + * Increase the time corresponding to one jiffy. */ usec_count = 1000000/HZ; } @@ -106,17 +117,15 @@ unsigned long do_slow_gettimeoffset(void) */ /* This gives us 1.3 ms to do something useful when the NMI comes */ -/* right now, starting the watchdog is the same as resetting it */ +/* Right now, starting the watchdog is the same as resetting it */ #define start_watchdog reset_watchdog #if defined(CONFIG_ETRAX_WATCHDOG) static short int watchdog_key = 42; /* arbitrary 7 bit number */ #endif -/* number of pages to consider "out of memory". it is normal that the memory - * is used though, so put this really low. - */ - +/* Number of pages to consider "out of memory". It is normal that the memory + * is used though, so set this really low. */ #define WATCHDOG_MIN_FREE_PAGES 8 void @@ -125,14 +134,15 @@ reset_watchdog(void) #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; - /* only keep watchdog happy as long as we have memory left! */ + /* Only keep watchdog happy as long as we have memory left! */ if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { - /* reset the watchdog with the inverse of the old key */ - watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */ + /* Reset the watchdog with the inverse of the old key */ + /* Invert key, which is 7 bits */ + watchdog_key ^= ETRAX_WD_KEY_MASK; wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_start; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif } @@ -148,7 +158,7 @@ stop_watchdog(void) wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_stop; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); #endif } @@ -160,17 +170,28 @@ handle_watchdog_bite(struct pt_regs* regs) #if defined(CONFIG_ETRAX_WATCHDOG) extern int cause_of_death; - raw_printk("Watchdog bite\n"); + oops_in_progress = 1; + printk(KERN_WARNING "Watchdog bite\n"); /* Check if forced restart or unexpected watchdog */ if (cause_of_death == 0xbedead) { +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + /* There is a bug in Artpec-3 (voodoo TR 78) that requires + * us to go to lower frequency for the reset to be reliable + */ + reg_clkgen_rw_clk_ctrl ctrl = + REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl); +#endif while(1); } - /* Unexpected watchdog, stop the watchdog and dump registers*/ + /* Unexpected watchdog, stop the watchdog and dump registers. */ stop_watchdog(); - raw_printk("Oops: bitten by watchdog\n"); - show_registers(regs); + printk(KERN_WARNING "Oops: bitten by watchdog\n"); + show_registers(regs); + oops_in_progress = 0; #ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY reset_watchdog(); #endif @@ -178,21 +199,19 @@ handle_watchdog_bite(struct pt_regs* regs) #endif } -/* last time the cmos clock got updated */ +/* Last time the cmos clock got updated. */ static long last_rtc_update = 0; /* * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick + * as well as call the "do_timer()" routine every clocktick. */ - -//static unsigned short myjiff; /* used by our debug routine print_timestamp */ - extern void cris_do_profile(struct pt_regs *regs); static inline irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_interrupt(int irq, void *dev_id) { + struct pt_regs *regs = get_irq_regs(); int cpu = smp_processor_id(); reg_timer_r_masked_intr masked_intr; reg_timer_rw_ack_intr ack_intr = { 0 }; @@ -202,11 +221,11 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (!masked_intr.tmr0) return IRQ_NONE; - /* acknowledge the timer irq */ + /* Acknowledge the timer irq. */ ack_intr.tmr0 = 1; REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr); - /* reset watchdog otherwise it resets us! */ + /* Reset watchdog otherwise it resets us! */ reset_watchdog(); /* Update statistics. */ @@ -218,7 +237,7 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (cpu != 0) return IRQ_HANDLED; - /* call the real timer interrupt handler */ + /* Call the real timer interrupt handler */ do_timer(1); /* @@ -236,17 +255,17 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + /* Do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; } return IRQ_HANDLED; } -/* timer is IRQF_SHARED so drivers can add stuff to the timer irq chain - * it needs to be IRQF_DISABLED to make the jiffies update work properly +/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. + * It needs to be IRQF_DISABLED to make the jiffies update work properly. */ - -static struct irqaction irq_timer = { - .mask = timer_interrupt, +static struct irqaction irq_timer = { + .handler = timer_interrupt, .flags = IRQF_SHARED | IRQF_DISABLED, .mask = CPU_MASK_NONE, .name = "timer" @@ -256,27 +275,27 @@ void __init cris_timer_init(void) { int cpu = smp_processor_id(); - reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; - reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; + reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; + reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; reg_timer_rw_intr_mask timer_intr_mask; - /* Setup the etrax timers + /* Setup the etrax timers. * Base frequency is 100MHz, divider 1000000 -> 100 HZ * We use timer0, so timer1 is free. * The trig timer is used by the fasttimer API if enabled. */ - tmr0_ctrl.op = regk_timer_ld; + tmr0_ctrl.op = regk_timer_ld; tmr0_ctrl.freq = regk_timer_f100; - REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ - tmr0_ctrl.op = regk_timer_run; - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ - - /* enable the timer irq */ - timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); - timer_intr_mask.tmr0 = 1; - REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); + REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ + tmr0_ctrl.op = regk_timer_run; + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ + + /* Enable the timer irq. */ + timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); + timer_intr_mask.tmr0 = 1; + REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); } void __init @@ -284,7 +303,7 @@ time_init(void) { reg_intr_vect_rw_mask intr_mask; - /* probe for the RTC and read it if it exists + /* Probe for the RTC and read it if it exists. * Before the RTC can be probed the loops_per_usec variable needs * to be initialized to make usleep work. A better value for * loops_per_usec is calculated by the kernel later once the @@ -293,52 +312,74 @@ time_init(void) loops_per_usec = 50; if(RTC_INIT() < 0) { - /* no RTC, start at 1980 */ + /* No RTC, start at 1980 */ xtime.tv_sec = 0; xtime.tv_nsec = 0; have_rtc = 0; } else { - /* get the current time */ + /* Get the current time */ have_rtc = 1; update_xtime_from_cmos(); } /* - * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the - * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC). + * Initialize wall_to_monotonic such that adding it to + * xtime will yield zero, the tv_nsec field must be normalized + * (i.e., 0 <= nsec < NSEC_PER_SEC). */ set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - /* Start CPU local timer */ + /* Start CPU local timer. */ cris_timer_init(); - /* enable the timer irq in global config */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.timer = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - - /* now actually register the timer irq handler that calls timer_interrupt() */ + /* Enable the timer irq in global config. */ + intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1); + intr_mask.timer0 = 1; + REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask); - setup_irq(TIMER_INTR_VECT, &irq_timer); + /* Now actually register the timer irq handler that calls + * timer_interrupt(). */ + setup_irq(TIMER0_INTR_VECT, &irq_timer); - /* enable watchdog if we should use one */ + /* Enable watchdog if we should use one. */ #if defined(CONFIG_ETRAX_WATCHDOG) - printk("Enabling watchdog...\n"); + printk(KERN_INFO "Enabling watchdog...\n"); start_watchdog(); /* If we use the hardware watchdog, we want to trap it as an NMI - and dump registers before it resets us. For this to happen, we - must set the "m" NMI enable flag (which once set, is unset only - when an NMI is taken). - - The same goes for the external NMI, but that doesn't have any - driver or infrastructure support yet. */ - { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); - } + * and dump registers before it resets us. For this to happen, we + * must set the "m" NMI enable flag (which once set, is unset only + * when an NMI is taken). */ + { + unsigned long flags; + local_save_flags(flags); + flags |= (1<<30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); + } +#endif + +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&cris_time_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); #endif } + +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_POSTCHANGE) { + reg_timer_r_tmr0_data data; + reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ; + do { + data = REG_RD(timer, timer_regs[freqs->cpu], + r_tmr0_data); + } while (data > 20); + REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div); + } + return 0; +} +#endif diff --git a/arch/cris/arch-v32/kernel/traps.c b/arch/cris/arch-v32/kernel/traps.c index 17fd3dbd1c8..9003e382cad 100644 --- a/arch/cris/arch-v32/kernel/traps.c +++ b/arch/cris/arch-v32/kernel/traps.c @@ -1,50 +1,45 @@ /* - * Copyright (C) 2003, Axis Communications AB. + * Copyright (C) 2003-2006, Axis Communications AB. */ #include <linux/ptrace.h> +#include <linux/module.h> #include <asm/uaccess.h> - -#include <asm/arch/hwregs/supp_reg.h> - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - -extern int raw_printk(const char *fmt, ...); +#include <hwregs/supp_reg.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/irq.h> void show_registers(struct pt_regs *regs) { /* * It's possible to use either the USP register or current->thread.usp. - * USP might not correspond to the current proccess for all cases this + * USP might not correspond to the current process for all cases this * function is called, and current->thread.usp isn't up to date for the - * current proccess. Experience shows that using USP is the way to go. + * current process. Experience shows that using USP is the way to go. */ - unsigned long usp; + unsigned long usp = rdusp(); unsigned long d_mmu_cause; unsigned long i_mmu_cause; - usp = rdusp(); + printk("CPU: %d\n", smp_processor_id()); - raw_printk("CPU: %d\n", smp_processor_id()); + printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); - raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", - regs->erp, regs->srp, regs->ccs, usp, regs->mof); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", - regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", - regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", - regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs->acr); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs->acr); - - raw_printk("sp: %08lx\n", regs); + printk(" sp: %08lx\n", (unsigned long)regs); SUPP_BANK_SEL(BANK_IM); SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause); @@ -52,18 +47,20 @@ show_registers(struct pt_regs *regs) SUPP_BANK_SEL(BANK_DM); SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause); - raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause); - raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); + printk(" Data MMU Cause: %08lx\n", d_mmu_cause); + printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); - raw_printk("Process %s (pid: %d, stackpage: %08lx)\n", - current->comm, current->pid, (unsigned long) current); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); - /* Show additional info if in kernel-mode. */ + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ if (!user_mode(regs)) { int i; - unsigned char c; - show_stack(NULL, (unsigned long *) usp); + show_stack(NULL, (unsigned long *)usp); /* * If the previous stack-dump wasn't a kernel one, dump the @@ -72,7 +69,7 @@ show_registers(struct pt_regs *regs) if (usp != 0) show_stack(NULL, NULL); - raw_printk("\nCode: "); + printk("\nCode: "); if (regs->erp < PAGE_OFFSET) goto bad_value; @@ -84,76 +81,115 @@ show_registers(struct pt_regs *regs) * instruction decoding should be in sync at the interesting * point, but small enough to fit on a row. The regs->erp * location is pointed out in a ksymoops-friendly way by - * wrapping the byte for that address in parenthesis. + * wrapping the byte for that address in parenthesises. */ for (i = -12; i < 12; i++) { - if (__get_user(c, &((unsigned char *) regs->erp)[i])) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->erp)[i])) { bad_value: - raw_printk(" Bad IP value."); + printk(" Bad IP value."); break; } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); + printk("%02x ", c); } - - raw_printk("\n"); + printk("\n"); } } -/* - * This gets called from entry.S when the watchdog has bitten. Show something - * similar to an Oops dump, and if the kernel is configured to be a nice doggy; - * halt instead of reboot. - */ void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); + unsigned long flags; - while (1) - ; /* Do nothing. */ -#else - show_registers(regs); -#endif + local_save_flags(flags); + flags |= (1 << 30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); } -/* This is normally the Oops function. */ -void -die_if_kernel(const char *str, struct pt_regs *regs, long err) +extern void (*nmi_handler)(struct pt_regs *); +void handle_nmi(struct pt_regs *regs) { - if (user_mode(regs)) - return; +#ifdef CONFIG_ETRAXFS + reg_intr_vect_r_nmi r; +#endif -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* - * This printout might take too long and could trigger - * the watchdog normally. If NICE_DOGGY is set, simply - * stop the watchdog during the printout. - */ - stop_watchdog(); + if (nmi_handler) + nmi_handler(regs); + +#ifdef CONFIG_ETRAXFS + /* Wait until nmi is no longer active. */ + do { + r = REG_RD(intr_vect, regi_irq, r_nmi); + } while (r.ext == regk_intr_vect_on); #endif +} - raw_printk("%s: %04lx\n", str, err & 0xffff); - show_registers(regs); +#ifdef CONFIG_BUG +extern void die_if_kernel(const char *str, struct pt_regs *regs, long err); -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif +/* Copy of the regs at BUG() time. */ +struct pt_regs BUG_regs; - do_exit(SIGSEGV); +void do_BUG(char *file, unsigned int line) +{ + printk("kernel BUG at %s:%d!\n", file, line); + die_if_kernel("Oops", &BUG_regs, 0); } +EXPORT_SYMBOL(do_BUG); -void arch_enable_nmi(void) +void fixup_BUG(struct pt_regs *regs) { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); + BUG_regs = *regs; + +#ifdef CONFIG_DEBUG_BUGVERBOSE + /* + * Fixup the BUG arguments through exception handlers. + */ + { + const struct exception_table_entry *fixup; + + /* + * ERP points at the "break 14" + 2, compensate for the 2 + * bytes. + */ + fixup = search_exception_tables(instruction_pointer(regs) - 2); + if (fixup) { + /* Adjust the instruction pointer in the stackframe. */ + instruction_pointer(regs) = fixup->fixup; + arch_fixup(regs); + } + } +#else + /* Dont try to lookup the filename + line, just dump regs. */ + do_BUG("unknown", 0); +#endif } + +/* + * Break 14 handler. Save regs and jump into the fixup_BUG. + */ +__asm__ ( ".text\n\t" + ".global breakh_BUG\n\t" + "breakh_BUG:\n\t" + SAVE_ALL + KGDB_FIXUP + "move.d $sp, $r10\n\t" + "jsr fixup_BUG\n\t" + "nop\n\t" + "jump ret_from_intr\n\t" + "nop\n\t"); + + +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) +{ +} +#endif +#endif diff --git a/arch/cris/arch-v32/kernel/vcs_hook.c b/arch/cris/arch-v32/kernel/vcs_hook.c deleted file mode 100644 index 64d71c54c22..00000000000 --- a/arch/cris/arch-v32/kernel/vcs_hook.c +++ /dev/null @@ -1,96 +0,0 @@ -// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook. This is the part running in the -// simulated program. -// - -#include "vcs_hook.h" -#include <stdarg.h> -#include <asm/arch-v32/hwregs/reg_map.h> -#include <asm/arch-v32/hwregs/intr_vect_defs.h> - -#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ -#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ - -#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0) -#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset] - - -// ------------------------------------------------------------------ hook_call -int hook_call( unsigned id, unsigned pcnt, ...) { - va_list ap; - unsigned i; - unsigned ret; -#ifdef USING_SOS - PREEMPT_OFF_SAVE(); -#endif - - // pass parameters - HOOK_DATA(0) = id; - - /* Have to make hook_print_str a special case since we call with a - parameter of byte type. Should perhaps be a separate - hook_call. */ - - if (id == hook_print_str) { - int i; - char *str; - - HOOK_DATA(1) = pcnt; - - va_start(ap, pcnt); - str = (char*)va_arg(ap,unsigned); - - for (i=0; i!=pcnt; i++) { - HOOK_DATA_BYTE(8+i) = str[i]; - } - HOOK_DATA_BYTE(8+i) = 0; /* null byte */ - } - else { - va_start(ap, pcnt); - for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned); - va_end(ap); - } - - // read from mem to make sure data has propagated to memory before trigging - *((volatile unsigned*) HOOK_MEM_BASE_ADDR); - - // trigger hook - HOOK_TRIG(id); - - // wait for call to finish - while( VHOOK_DATA(0) > 0 ) {} - - // extract return value - - ret = VHOOK_DATA(1); - -#ifdef USING_SOS - PREEMPT_RESTORE(); -#endif - return ret; -} - -unsigned -hook_buf(unsigned i) -{ - return (HOOK_DATA(i)); -} - -void print_str( const char *str ) { - int i; - for (i=1; str[i]; i++); /* find null at end of string */ - hook_call(hook_print_str, i, str); -} - -// --------------------------------------------------------------- CPU_KICK_DOG -void CPU_KICK_DOG(void) { - (void) hook_call( hook_kick_dog, 0 ); -} - -// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT -void CPU_WATCHDOG_TIMEOUT( unsigned t ) { - (void) hook_call( hook_dog_timeout, 1, t ); -} diff --git a/arch/cris/arch-v32/lib/Makefile b/arch/cris/arch-v32/lib/Makefile index 05b3ec6978d..eb4aad1f115 100644 --- a/arch/cris/arch-v32/lib/Makefile +++ b/arch/cris/arch-v32/lib/Makefile @@ -2,5 +2,6 @@ # Makefile for Etrax-specific library files.. # -lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o +lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o \ + csumcpfruser.o spinlock.o delay.o diff --git a/arch/cris/arch-v32/lib/checksum.S b/arch/cris/arch-v32/lib/checksum.S index 32e66181b82..87f3fd71ab1 100644 --- a/arch/cris/arch-v32/lib/checksum.S +++ b/arch/cris/arch-v32/lib/checksum.S @@ -1,6 +1,6 @@ /* * A fast checksum routine using movem - * Copyright (c) 1998-2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * csum_partial(const unsigned char * buff, int len, unsigned int sum) */ @@ -12,30 +12,23 @@ csum_partial: ;; r11 - length ;; r12 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r11 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r11 + blt _word_loop + move.d $r11, $acr subq 9*4,$sp - subq 10*4,$r11 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem checksum _mloop: movem [$r10+],$r9 ; read 10 longwords - + ;; Loop count without touching the c flag. + addoq -10*4, $acr, $acr ;; perform dword checksumming on the 10 longwords - add.d $r0,$r12 + addc $r0,$r12 addc $r1,$r12 addc $r2,$r12 addc $r3,$r12 @@ -46,60 +39,41 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r12 addc $r9,$r12 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r12 - addc 0,$r12 ; do it again, since we might have generated a carry - - subq 10*4,$r11 - bge _mloop - nop - - addq 10*4,$r11 ; compensate for last loop underflowing length + ;; test $acr without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r11 <= acr is not really needed in the mloop, just using the dslot + ;; to prepare for what is needed after mloop. + move.d $acr, $r11 + ;; fold the last carry into r13 + addc 0, $r12 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r12 - beq _no_fold - - ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below. - ;; r9 and r13 can be used as temporaries. + addq 10*4,$r11 ; compensate for last loop underflowing length moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9 lsrq 16,$r9 move.d $r12,$r13 lsrq 16,$r13 ; r13 = checksum >> 16 - and.d $r9,$r12 ; checksum = checksum & 0xffff - add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 + and.d $r9,$r12 ; checksum = checksum & 0xffff _no_fold: - cmpq 2,$r11 + subq 2,$r11 blt _no_words - nop + add.d $r13,$r12 ; checksum += r13 ;; checksum the rest of the words - - subq 2,$r11 - _wloop: subq 2,$r11 bge _wloop addu.w [$r10+],$r12 - addq 2,$r11 - _no_words: + addq 2,$r11 ;; see if we have one odd byte more - cmpq 1,$r11 - beq _do_byte + bne _do_byte nop ret move.d $r12,$r10 diff --git a/arch/cris/arch-v32/lib/checksumcopy.S b/arch/cris/arch-v32/lib/checksumcopy.S index 9303ccbadc6..21aabe91489 100644 --- a/arch/cris/arch-v32/lib/checksumcopy.S +++ b/arch/cris/arch-v32/lib/checksumcopy.S @@ -1,6 +1,6 @@ /* * A fast checksum+copy routine using movem - * Copyright (c) 1998, 2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * Authors: Bjorn Wesen * @@ -16,32 +16,23 @@ csum_partial_copy_nocheck: ;; r12 - length ;; r13 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r12 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r12 + blt _word_loop + move.d $r12, $acr subq 9*4,$sp - subq 10*4,$r12 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem copy and checksum - 1: ;; A failing userspace access (the read) will have this as PC. _mloop: movem [$r10+],$r9 ; read 10 longwords + addoq -10*4, $acr, $acr ; loop counter in latency cycle movem $r9,[$r11+] ; write 10 longwords ;; perform dword checksumming on the 10 longwords - - add.d $r0,$r13 + addc $r0,$r13 addc $r1,$r13 addc $r2,$r13 addc $r3,$r13 @@ -52,47 +43,30 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r13 addc $r9,$r13 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r13 - addc 0,$r13 ; do it again, since we might have generated a carry - - subq 10*4,$r12 - bge _mloop - nop - - addq 10*4,$r12 ; compensate for last loop underflowing length + ;; test $acr, without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r12 <= acr is needed after mloop and in the exception handlers. + move.d $acr, $r12 + ;; fold the last carry into r13 + addc 0, $r13 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r13 - beq _no_fold + addq 10*4,$r12 ; compensate for last loop underflowing length ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below ;; r9 can be used as temporary. - move.d $r13,$r9 lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff - add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 -_no_fold: - cmpq 2,$r12 + subq 2, $r12 blt _no_words - nop + add.d $r9,$r13 ; checksum += r0 ;; copy and checksum the rest of the words - - subq 2,$r12 - 2: ;; A failing userspace access for the read below will have this as PC. _wloop: move.w [$r10+],$r9 addu.w $r9,$r13 @@ -100,12 +74,9 @@ _wloop: move.w [$r10+],$r9 bge _wloop move.w $r9,[$r11+] - addq 2,$r12 - _no_words: - ;; see if we have one odd byte more - cmpq 1,$r12 - beq _do_byte + addq 2,$r12 + bne _do_byte nop ret move.d $r13,$r10 diff --git a/arch/cris/arch-v32/lib/delay.c b/arch/cris/arch-v32/lib/delay.c new file mode 100644 index 00000000000..39f1ac9995b --- /dev/null +++ b/arch/cris/arch-v32/lib/delay.c @@ -0,0 +1,28 @@ +/* + * Precise Delay Loops for ETRAX FS + * + * Copyright (C) 2006 Axis Communications AB. + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/module.h> + +/* + * On ETRAX FS, we can check the free-running read-only 100MHz timer + * getting 32-bit 10ns precision, theoretically good for 42.94967295 + * seconds. Unsigned arithmetic and careful expression handles + * wrapping. + */ + +void cris_delay10ns(u32 n10ns) +{ + u32 t0 = REG_RD(timer, regi_timer0, r_time); + while (REG_RD(timer, regi_timer0, r_time) - t0 < n10ns) + ; +} +EXPORT_SYMBOL(cris_delay10ns); diff --git a/arch/cris/arch-v32/lib/spinlock.S b/arch/cris/arch-v32/lib/spinlock.S index 2437ae7f6ed..79087ef59a1 100644 --- a/arch/cris/arch-v32/lib/spinlock.S +++ b/arch/cris/arch-v32/lib/spinlock.S @@ -12,11 +12,11 @@ cris_spin_lock: clearf p -1: test.d [$r10] +1: test.b [$r10] beq 1b clearf p ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret @@ -24,10 +24,10 @@ cris_spin_lock: cris_spin_trylock: clearf p -1: move.d [$r10], $r11 +1: move.b [$r10], $r11 ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret - move.d $r11,$r10 + movu.b $r11,$r10 diff --git a/arch/cris/arch-v32/mach-a3/Kconfig b/arch/cris/arch-v32/mach-a3/Kconfig new file mode 100644 index 00000000000..a4df06d5997 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Kconfig @@ -0,0 +1,110 @@ +if CRIS_MACH_ARTPEC3 + +menu "Artpec-3 options" + depends on CRIS_MACH_ARTPEC3 + +config ETRAX_DRAM_VIRTUAL_BASE + hex + default "c0000000" + +config ETRAX_L2CACHE + bool + default y + +config ETRAX_SERIAL_PORTS + int + default 5 + +config ETRAX_DDR + bool + default y + +config ETRAX_DDR2_MRS + hex "DDR2 MRS" + default "0" + +config ETRAX_DDR2_TIMING + hex "DDR2 SDRAM timing" + default "0" + help + SDRAM timing parameters. + +config ETRAX_DDR2_CONFIG + hex "DDR2 config" + default "0" + +config ETRAX_PIO_CE0_CFG + hex "PIO CE0 configuration" + default "0" + +config ETRAX_PIO_CE1_CFG + hex "PIO CE1 configuration" + default "0" + +config ETRAX_PIO_CE2_CFG + hex "PIO CE2 configuration" + default "0" + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + default "00000000" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PA_OUT + hex "GIO_PA_OUT" + default "00000000" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_GIO_PB_OE + hex "GIO_PB_OE" + default "000000000" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PB_OUT + hex "GIO_PB_OUT" + default "000000000" + help + Configures the initial data for the general port B bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PC_OE + hex "GIO_PC_OE" + default "00000" + help + Configures the direction of general port C bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PC_OUT + hex "GIO_PC_OUT" + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-a3/Makefile b/arch/cris/arch-v32/mach-a3/Makefile new file mode 100644 index 00000000000..41fa6a6893a --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +obj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-a3/arbiter.c b/arch/cris/arch-v32/mach-a3/arbiter.c new file mode 100644 index 00000000000..8b924db71c9 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/arbiter.c @@ -0,0 +1,634 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + * + * The artpec-3 has two arbiters. The memory hierarchy looks like this: + * + * + * CPU DMAs + * | | + * | | + * -------------- ------------------ + * | foo arbiter|----| Internal memory| + * -------------- ------------------ + * | + * -------------- + * | L2 cache | + * -------------- + * | + * h264 etc | + * | | + * | | + * -------------- + * | bar arbiter| + * -------------- + * | + * --------- + * | SDRAM | + * --------- + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_foo_defs.h> +#include <hwregs/marb_bar_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +#define D(x) + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define SDRAM_BANDWIDTH 400000000 +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_SLOTS 64 +#define NBR_OF_REGIONS 2 +#define NBR_OF_CLIENTS 15 +#define ARBITERS 2 +#define UNASSIGNED 100 + +struct arbiter { + unsigned long instance; + int nbr_regions; + int nbr_clients; + int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; + int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +}; + +static struct crisv32_watch_entry watches[ARBITERS][NUMBER_OF_BP] = +{ + { + {regi_marb_foo_bp0}, + {regi_marb_foo_bp1}, + {regi_marb_foo_bp2}, + {regi_marb_foo_bp3} + }, + { + {regi_marb_bar_bp0}, + {regi_marb_bar_bp1}, + {regi_marb_bar_bp2}, + {regi_marb_bar_bp3} + } +}; + +struct arbiter arbiters[ARBITERS] = +{ + { /* L2 cache arbiter */ + .instance = regi_marb_foo, + .nbr_regions = 2, + .nbr_clients = 15 + }, + { /* DDR2 arbiter */ + .instance = regi_marb_bar, + .nbr_regions = 1, + .nbr_clients = 9 + } +}; + +static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id); +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int arbiter, int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < arbiters[arbiter].nbr_clients; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!arbiters[arbiter].requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!arbiters[arbiter].active_clients[region][client] || + unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = NBR_OF_SLOTS / + arbiters[arbiter].requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!arbiters[arbiter].active_clients[region][client]) { + client = (client + 1) % + arbiters[arbiter].nbr_clients; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % arbiters[arbiter].nbr_clients; + } + if (arbiter == 0) { + if (region == EXT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_l2_slots, slot, val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_intm_slots, slot, val[slot]); + } else { + REG_WR_INT_VECT(marb_bar, regi_marb_bar, + rw_ddr2_slots, slot, val[slot]); + } + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + arbiters[0].active_clients[EXT_REGION][11] = 1; + arbiters[0].active_clients[EXT_REGION][12] = 1; + crisv32_arbiter_config(0, EXT_REGION, 0); + crisv32_arbiter_config(0, INT_REGION, 0); + crisv32_arbiter_config(1, EXT_REGION, 0); + + if (request_irq(MEMARB_FOO_INTR_VECT, crisv32_foo_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + + if (request_irq(MEMARB_BAR_INTR_VECT, crisv32_bar_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + MARB_CLIENTS(arbiter_all_clients, arbiter_bar_all_clients), + arbiter_all_write, NULL); +#endif + + /* Set up max burst sizes by default */ + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_ccd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vout_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_fifo_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_l2cache_burst, 3); +} + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + int arbiter = 0; + + crisv32_arbiter_init(); + + if (client & 0xffff0000) { + arbiter = 1; + client >>= 16; + } + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) { + total_assigned += arbiters[arbiter].requested_slots[region][i]; + total_clients += arbiters[arbiter].active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + arbiters[arbiter].active_clients[region][client] = 1; + arbiters[arbiter].requested_slots[region][client] = req; + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); + + /* Propagate allocation from foo to bar */ + if (arbiter == 0) + crisv32_arbiter_allocate_bandwidth(8 << 16, + EXT_REGION, bandwidth); + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + int arbiter = 0; + + if (client & 0xffff0000) + arbiter = 1; + + arbiters[arbiter].requested_slots[region][client] = 0; + arbiters[arbiter].active_clients[region][client] = 0; + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) + total_assigned += arbiters[arbiter].requested_slots[region][i]; + + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + int arbiter; + int used[2]; + int ret = 0; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + if (clients & 0xffff) + used[0] = 1; + if (clients & 0xffff0000) + used[1] = 1; + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + if (!used[arbiter]) + continue; + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[arbiter][i].used) { + unsigned intr_mask; + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, + regi_marb_bar, rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, + regi_marb_foo, rw_intr_mask); + + watches[arbiter][i].used = 1; + watches[arbiter][i].start = start; + watches[arbiter][i].end = start + size; + watches[arbiter][i].cb = cb; + + ret |= (i + 1) << (arbiter + 8); + if (arbiter) { + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_clients, + clients & 0xffff); + } else { + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_clients, clients >> 16); + } + + if (i == 0) + intr_mask |= 1; + else if (i == 1) + intr_mask |= 2; + else if (i == 2) + intr_mask |= 4; + else if (i == 3) + intr_mask |= 8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, + rw_intr_mask, intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, + rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + + break; + } + } + } + spin_unlock(&arbiter_lock); + if (ret) + return ret; + else + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + int arbiter; + int intr_mask; + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + int id2; + + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, regi_marb_bar, + rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, regi_marb_foo, + rw_intr_mask); + + id2 = (id & (0xff << (arbiter + 8))) >> (arbiter + 8); + if (id2 == 0) + continue; + id2--; + if ((id2 >= NUMBER_OF_BP) || (!watches[arbiter][id2].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[arbiter][id2], 0, + sizeof(struct crisv32_watch_entry)); + + if (id2 == 0) + intr_mask &= ~1; + else if (id2 == 1) + intr_mask &= ~2; + else if (id2 == 2) + intr_mask &= ~4; + else if (id2 == 3) + intr_mask &= ~8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, rw_intr_mask, + intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, rw_intr_mask, + intr_mask); + } + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_foo_r_masked_intr masked_intr = + REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + reg_marb_foo_bp_r_brk_clients r_clients; + reg_marb_foo_bp_r_brk_addr r_addr; + reg_marb_foo_bp_r_brk_op r_op; + reg_marb_foo_bp_r_brk_first_client r_first; + reg_marb_foo_bp_r_brk_size r_size; + reg_marb_foo_bp_rw_ack ack = {0}; + reg_marb_foo_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_foo_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_foo_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_foo_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_foo_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_foo_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_size, r_size)); + + REG_WR(marb_foo_bp, watch->instance, rw_ack, ack); + REG_WR(marb_foo, regi_marb_foo, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_bar_r_masked_intr masked_intr = + REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + reg_marb_bar_bp_r_brk_clients r_clients; + reg_marb_bar_bp_r_brk_addr r_addr; + reg_marb_bar_bp_r_brk_op r_op; + reg_marb_bar_bp_r_brk_first_client r_first; + reg_marb_bar_bp_r_brk_size r_size; + reg_marb_bar_bp_rw_ack ack = {0}; + reg_marb_bar_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bar_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bar_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bar_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bar_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bar_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_size, r_size)); + + REG_WR(marb_bar_bp, watch->instance, rw_ack, ack); + REG_WR(marb_bar, regi_marb_bar, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + diff --git a/arch/cris/arch-v32/mach-a3/cpufreq.c b/arch/cris/arch-v32/mach-a3/cpufreq.c new file mode 100644 index 00000000000..8e5a3cab8ad --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/cpufreq.c @@ -0,0 +1,153 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/ddr2_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i = 0; + struct cpufreq_freqs freqs; + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + +#ifdef CONFIG_SMP + for_each_present_cpu(i) +#endif + { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target(policy, cris_freq_table, + target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_ddr2_rw_cfg cfg = + REG_RD(ddr2, regi_ddr2_ctrl, rw_cfg); + cfg.ref_interval = (freqs->new == 200000 ? 1560 : 46); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++); + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/mach-a3/dma.c b/arch/cris/arch-v32/mach-a3/dma.c new file mode 100644 index 00000000000..25f236ef0b8 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dma.c @@ -0,0 +1,185 @@ +/* Wrapper for DMA channel allocator that starts clocks etc */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <asm/arch/mach/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/strmux_defs.h> +#include <linux/errno.h> +#include <asm/system.h> +#include <arbiter.h> + +static char used_dma_channels[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; + +static DEFINE_SPINLOCK(dma_lock); + +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) +{ + unsigned long flags; + reg_clkgen_rw_clk_ctrl clk_ctrl; + reg_strmux_rw_cfg strmux_cfg; + + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; + + spin_lock_irqsave(&dma_lock, flags); + + if (used_dma_channels[dmanr]) { + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); + return -EBUSY; + } + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); + + switch (dmanr) { + case 0: + case 1: + clk_ctrl.dma0_1_eth = 1; + break; + case 2: + case 3: + clk_ctrl.dma2_3_strcop = 1; + break; + case 4: + case 5: + clk_ctrl.dma4_5_iop = 1; + break; + case 6: + case 7: + clk_ctrl.sser_ser_dma6_7 = 1; + break; + case 9: + case 11: + clk_ctrl.dma9_11 = 1; + break; +#if MAX_DMA_CHANNELS-1 != 11 +#error Check dma.c +#endif + default: + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS-1); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + return -EINVAL; + } + + switch (owner) { + case dma_eth: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_eth; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_eth; + else + panic("Invalid DMA channel for eth\n"); + break; + case dma_ser0: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_ser0; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_ser0; + else + panic("Invalid DMA channel for ser0\n"); + break; + case dma_ser3: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ser3; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ser3; + else + panic("Invalid DMA channel for ser3\n"); + break; + case dma_strp: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_strcop; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_strcop; + else + panic("Invalid DMA channel for strp\n"); + break; + case dma_ser1: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_ser1; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_ser1; + else + panic("Invalid DMA channel for ser1\n"); + break; + case dma_iop: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_iop; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_iop; + else + panic("Invalid DMA channel for iop\n"); + break; + case dma_ser2: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_ser2; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_ser2; + else + panic("Invalid DMA channel for ser2\n"); + break; + case dma_sser: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_sser; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_sser; + else + panic("Invalid DMA channel for sser\n"); + break; + case dma_ser4: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_ser4; + else + panic("Invalid DMA channel for ser4\n"); + break; + case dma_jpeg: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_jpeg; + else + panic("Invalid DMA channel for JPEG\n"); + break; + case dma_h264: + if (dmanr == 11) + strmux_cfg.dma11 = regk_strmux_h264; + else + panic("Invalid DMA channel for H264\n"); + break; + } + + used_dma_channels[dmanr] = 1; + used_dma_channels_users[dmanr] = device_id; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); + spin_unlock_irqrestore(&dma_lock, flags); + return 0; +} + +void crisv32_free_dma(unsigned int dmanr) +{ + spin_lock(&dma_lock); + used_dma_channels[dmanr] = 0; + spin_unlock(&dma_lock); +} diff --git a/arch/cris/arch-v32/mach-a3/dram_init.S b/arch/cris/arch-v32/mach-a3/dram_init.S new file mode 100644 index 00000000000..94d6b41cb29 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dram_init.S @@ -0,0 +1,104 @@ +/* + * DDR SDRAM initialization - alter with care + * This file is intended to be included from other assembler files + * + * Note: This file may not modify r8 or r9 because they are used to + * carry information from the decompresser to the kernel + * + * Copyright (C) 2005-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +/* Just to be certain the config file is included, we include it here + * explicitely instead of depending on it being included in the file that + * uses this code. + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> + + ;; WARNING! The registers r8 and r9 are used as parameters carrying + ;; information from the decompressor (if the kernel was compressed). + ;; They should not be used in the code below. + + ;; Refer to ddr2 MDS for initialization sequence + + ; Start clock + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_cfg), $r0 + move.d REG_STATE(ddr2, rw_phy_cfg, en, yes), $r1 + move.d $r1, [$r0] + + ; Reset phy and start calibration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_ctrl), $r0 + move.d REG_STATE(ddr2, rw_phy_ctrl, rst, yes) | \ + REG_STATE(ddr2, rw_phy_ctrl, cal_rst, yes), $r1 + move.d $r1, [$r0] + move.d REG_STATE(ddr2, rw_phy_ctrl, cal_start, yes), $r1 + move.d $r1, [$r0] + + ; 2. Wait 200us + move.d 10000, $r2 +1: bne 1b + subq 1, $r2 + + ; Issue commands + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_ctrl), $r0 + move.d sdram_commands_start, $r2 +command_loop: + movu.b [$r2+], $r1 + movu.w [$r2+], $r3 +do_cmd: + lslq 16, $r1 + or.d $r3, $r1 + move.d $r1, [$r0] + cmp.d sdram_commands_end, $r2 + blo command_loop + nop + + ; Set timing + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing), $r0 + move.d CONFIG_ETRAX_DDR2_TIMING, $r1 + move.d $r1, [$r0] + + ; Set latency + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_latency), $r0 + move.d 0x13, $r1 + move.d $r1, [$r0] + + ; Set configuration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg), $r0 + move.d CONFIG_ETRAX_DDR2_CONFIG, $r1 + move.d $r1, [$r0] + + ba after_sdram_commands + nop + +sdram_commands_start: + .byte regk_ddr2_deselect + .word 0 + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_emrs2 + .word 0 + .byte regk_ddr2_emrs3 + .word 0 + .byte regk_ddr2_emrs + .word regk_ddr2_dll_en + .byte regk_ddr2_mrs + .word regk_ddr2_dll_rst + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_mrs + .word CONFIG_ETRAX_DDR2_MRS & 0xffff + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_default | regk_ddr2_dll_en + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_exit | regk_ddr2_dll_en | (CONFIG_ETRAX_DDR2_MRS >> 16) +sdram_commands_end: + .align 1 +after_sdram_commands: diff --git a/arch/cris/arch-v32/mach-a3/hw_settings.S b/arch/cris/arch-v32/mach-a3/hw_settings.S new file mode 100644 index 00000000000..258a6329cd4 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/hw_settings.S @@ -0,0 +1,51 @@ +/* + * This table is used by some tools to extract hardware parameters. + * The table should be included in the kernel and the decompressor. + * Don't forget to update the tools if you change this table. + * + * Copyright (C) 2001-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> + + .ascii "HW_PARAM_MAGIC" ; Magic number + .dword 0xc0004000 ; Kernel start address + + ; Debug port +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + .dword 0 +#elif defined(CONFIG_ETRAX_DEBUG_PORT1) + .dword 1 +#elif defined(CONFIG_ETRAX_DEBUG_PORT2) + .dword 2 +#elif defined(CONFIG_ETRAX_DEBUG_PORT3) + .dword 3 +#else + .dword 4 ; No debug +#endif + + ; Register values + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg) + .dword CONFIG_ETRAX_DDR2_CONFIG + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing) + .dword CONFIG_ETRAX_DDR2_TIMING + .dword CONFIG_ETRAX_DDR2_MRS + + .dword REG_ADDR(gio, regi_gio, rw_pa_dout) + .dword CONFIG_ETRAX_DEF_GIO_PA_OUT + .dword REG_ADDR(gio, regi_gio, rw_pa_oe) + .dword CONFIG_ETRAX_DEF_GIO_PA_OE + .dword REG_ADDR(gio, regi_gio, rw_pb_dout) + .dword CONFIG_ETRAX_DEF_GIO_PB_OUT + .dword REG_ADDR(gio, regi_gio, rw_pb_oe) + .dword CONFIG_ETRAX_DEF_GIO_PB_OE + .dword REG_ADDR(gio, regi_gio, rw_pc_dout) + .dword CONFIG_ETRAX_DEF_GIO_PC_OUT + .dword REG_ADDR(gio, regi_gio, rw_pc_oe) + .dword CONFIG_ETRAX_DEF_GIO_PC_OE + + .dword 0 ; No more register values diff --git a/arch/cris/arch-v32/mach-a3/io.c b/arch/cris/arch-v32/mach-a3/io.c new file mode 100644 index 00000000000..9eeaf3eca47 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/io.c @@ -0,0 +1,149 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/mach/pinmux.h> +#include <hwregs/gio_defs.h> + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 16 + }, +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 32 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif + diff --git a/arch/cris/arch-v32/mach-a3/pinmux.c b/arch/cris/arch-v32/mach-a3/pinmux.c new file mode 100644 index 00000000000..0a28c9bedfb --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/pinmux.c @@ -0,0 +1,386 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> +#include <hwregs/clkgen_defs.h> + +#undef DEBUG + +#define PINS 80 +#define PORT_PINS 32 +#define PORTS 3 + +static char pins[PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int +crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + crisv32_pinmux_alloc(PORT_A, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_B, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port >= PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port * PORT_PINS + i] != pinmux_none) && + (pins[port * PORT_PINS + i] != pinmux_gpio) && + (pins[port * PORT_PINS + i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen, + rw_clk_ctrl); + + switch (function) { + case pinmux_eth: + clk_ctrl.eth = regk_clkgen_yes; + clk_ctrl.dma0_1_eth = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 8, 23, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_B, 24, 25, pinmux_fixed); + hwprot.eth = hwprot.eth_mdio = regk_pinmux_yes; + break; + case pinmux_geth: + ret = crisv32_pinmux_alloc(PORT_B, 0, 7, pinmux_fixed); + hwprot.geth = regk_pinmux_yes; + break; + case pinmux_tg_cmos: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 29, pinmux_fixed); + hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_tg_ccd: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 31, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_fixed); + hwprot.tg = hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_vout: + clk_ctrl.strdma0_2_video = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 8, 18, pinmux_fixed); + hwprot.vout = hwprot.vout_sync = regk_pinmux_yes; + break; + case pinmux_ser1: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 24, 25, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 26, 27, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 28, 29, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_ser4: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 30, 31, pinmux_fixed); + hwprot.ser4 = regk_pinmux_yes; + break; + case pinmux_sser: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 19, 23, pinmux_fixed); + hwprot.sser = regk_pinmux_yes; + break; + case pinmux_pio: + hwprot.pio = regk_pinmux_yes; + ret = 0; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_alloc(PORT_A, 30, 30, pinmux_fixed); + hwprot.pwm0 = regk_pinmux_yes; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_alloc(PORT_A, 31, 31, pinmux_fixed); + hwprot.pwm1 = regk_pinmux_yes; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_alloc(PORT_B, 26, 26, pinmux_fixed); + hwprot.pwm2 = regk_pinmux_yes; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_alloc(PORT_A, 0, 1, pinmux_fixed); + hwprot.i2c0 = regk_pinmux_yes; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + hwprot.i2c1 = regk_pinmux_yes; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 7, 7, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_yes; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 4, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda1 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 5, 5, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda2 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 6, 6, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda3 = regk_pinmux_yes; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + } else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + int pin = port * PORT_PINS; + + for (i = 0; (i < PORT_PINS) && (pin < PINS); i++, pin++) { + if (pins[pin] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[pin] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_gio_pa + 4 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_iop_pa + 4 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int +crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_eth: + ret = crisv32_pinmux_dealloc(PORT_B, 8, 23); + ret |= crisv32_pinmux_dealloc(PORT_B, 24, 25); + ret |= crisv32_pinmux_dealloc(PORT_B, 0, 7); + hwprot.eth = hwprot.eth_mdio = hwprot.geth = regk_pinmux_no; + break; + case pinmux_tg_cmos: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 29); + hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_tg_ccd: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 31); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 15); + hwprot.tg = hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_vout: + ret = crisv32_pinmux_dealloc(PORT_A, 8, 18); + hwprot.vout = hwprot.vout_sync = regk_pinmux_no; + break; + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_A, 24, 25); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_A, 26, 27); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_A, 28, 29); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_ser4: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 31); + hwprot.ser4 = regk_pinmux_no; + break; + case pinmux_sser: + ret = crisv32_pinmux_dealloc(PORT_A, 19, 23); + hwprot.sser = regk_pinmux_no; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 30); + hwprot.pwm0 = regk_pinmux_no; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_dealloc(PORT_A, 31, 31); + hwprot.pwm1 = regk_pinmux_no; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_dealloc(PORT_B, 26, 26); + hwprot.pwm2 = regk_pinmux_no; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_dealloc(PORT_A, 0, 1); + hwprot.i2c0 = regk_pinmux_no; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + hwprot.i2c1 = regk_pinmux_no; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 7, 7); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_no; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 4); + hwprot.i2c1_sda1 = regk_pinmux_no; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 5, 5); + hwprot.i2c1_sda2 = regk_pinmux_no; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 6, 6); + hwprot.i2c1_sda3 = regk_pinmux_no; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_dump(void) +{ + int i, j; + int pin = 0; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + pin++; + printk(KERN_DEBUG "Port %c\n", 'A'+i); + for (j = 0; (j < PORT_PINS) && (pin < PINS); j++, pin++) + printk(KERN_DEBUG + " Pin %d = %d\n", j, pins[i * PORT_PINS + j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.c b/arch/cris/arch-v32/mach-a3/vcs_hook.c new file mode 100644 index 00000000000..58b1a5469fd --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.c @@ -0,0 +1,103 @@ +/* + * Simulator hook mechanism + */ + +#include "vcs_hook.h" +#include <asm/io.h> +#include <stdarg.h> + +#define HOOK_TRIG_ADDR 0xb7000000 +#define HOOK_MEM_BASE_ADDR 0xce000000 + +static volatile unsigned *hook_base; + +#define HOOK_DATA(offset) hook_base[offset] +#define VHOOK_DATA(offset) hook_base[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)hook_base)[offset] + +static void hook_init(void) +{ + static int first = 1; + if (first) { + first = 0; + hook_base = ioremap(HOOK_MEM_BASE_ADDR, 8192); + } +} + +static unsigned hook_trig(unsigned id) +{ + unsigned ret; + + /* preempt_disable(); */ + + /* Dummy read from mem to make sure data has propagated to memory + * before trigging */ + ret = *hook_base; + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + + return ret; +} + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + + ret = hook_trig(id); + + return ret; +} + +int hook_call_str(unsigned id, unsigned size, const char *str) +{ + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + HOOK_DATA(1) = size; + + for (i = 0; i < size; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + HOOK_DATA_BYTE(8 + i) = 0; + + ret = hook_trig(id); + + return ret; +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ +} diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.h b/arch/cris/arch-v32/mach-a3/vcs_hook.h new file mode 100644 index 00000000000..8b73d0e8392 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.h @@ -0,0 +1,58 @@ +/* + * Simulator hook call mechanism + */ + +#ifndef __hook_h__ +#define __hook_h__ + +int hook_call(unsigned id, unsigned pcnt, ...); +int hook_call_str(unsigned id, unsigned size, const char *str); + +enum hook_ids { + hook_debug_on = 1, + hook_debug_off, + hook_stop_sim_ok, + hook_stop_sim_fail, + hook_alloc_shared, + hook_ptr_shared, + hook_free_shared, + hook_file2shared, + hook_cmp_shared, + hook_print_params, + hook_sim_time, + hook_stop_sim, + hook_kick_dog, + hook_dog_timeout, + hook_rand, + hook_srand, + hook_rand_range, + hook_print_str, + hook_print_hex, + hook_cmp_offset_shared, + hook_fill_random_shared, + hook_alloc_random_data, + hook_calloc_random_data, + hook_print_int, + hook_print_uint, + hook_fputc, + hook_init_fd, + hook_sbrk, + hook_print_context_descr, + hook_print_data_descr, + hook_print_group_descr, + hook_fill_shared, + hook_sl_srand, + hook_sl_rand_irange, + hook_sl_rand_urange, + hook_sl_sh_malloc_aligned, + hook_sl_sh_calloc_aligned, + hook_sl_sh_alloc_random_data, + hook_sl_sh_file2mem, + hook_sl_vera_mbox_handle, + hook_sl_vera_mbox_put, + hook_sl_vera_mbox_get, + hook_sl_system, + hook_sl_sh_hexdump +}; + +#endif diff --git a/arch/cris/arch-v32/mach-fs/Kconfig b/arch/cris/arch-v32/mach-fs/Kconfig new file mode 100644 index 00000000000..f6d74475f1c --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Kconfig @@ -0,0 +1,216 @@ +if ETRAXFS + +menu "ETRAX FS options" + depends on ETRAXFS + +config ETRAX_DRAM_VIRTUAL_BASE + hex + depends on ETRAX_ARCH_V32 + default "c0000000" + +config ETRAX_SERIAL_PORTS + int + default 4 + +config ETRAX_MEM_GRP1_CONFIG + hex "MEM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "4044a" + help + Waitstates for flash. The default value is suitable for the + standard flashes used in axis products (120 ns). + +config ETRAX_MEM_GRP2_CONFIG + hex "MEM_GRP2_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for SRAM. 0 is a good choice for most Axis products. + +config ETRAX_MEM_GRP3_CONFIG + hex "MEM_GRP3_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP0-3. 0 is a good choice for most Axis products. + It may need to be changed if external devices such as extra + register-mapped LEDs are used. + +config ETRAX_MEM_GRP4_CONFIG + hex "MEM_GRP4_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP4-6. 0 is a good choice for most Axis products. + +config ETRAX_SDRAM_GRP0_CONFIG + hex "SDRAM_GRP0_CONFIG" + depends on ETRAX_ARCH_V32 + default "336" + help + SDRAM configuration for group 0. The value depends on the + hardware configuration. The default value is suitable + for 32 MB organized as two 16 bits chips (e.g. Axis + part number 18550) connected as one 32 bit device (i.e. in + the same group). + +config ETRAX_SDRAM_GRP1_CONFIG + hex "SDRAM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM configuration for group 1. The defult value is 0 + because group 1 is not used in the default configuration, + described in the help for SDRAM_GRP0_CONFIG. + +config ETRAX_SDRAM_TIMING + hex "SDRAM_TIMING" + depends on ETRAX_ARCH_V32 + default "104a" + help + SDRAM timing parameters. The default value is ok for + most hardwares but large SDRAMs may require a faster + refresh (a.k.a 8K refresh). The default value implies + 100MHz clock and SDR mode. + +config ETRAX_SDRAM_COMMAND + hex "SDRAM_COMMAND" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM command. Should be 0 unless you really know what + you are doing (may be != 0 for unusual address line + mappings such as in a MCM).. + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + depends on ETRAX_ARCH_V32 + default "1c" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PA_OUT + hex "GIO_PA_OUT" + depends on ETRAX_ARCH_V32 + default "00" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_GIO_PB_OE + hex "GIO_PB_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PB_OUT + hex "GIO_PB_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port B bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PC_OE + hex "GIO_PC_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port C bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PC_OUT + hex "GIO_PC_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PD_OE + hex "GIO_PD_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port D bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PD_OUT + hex "GIO_PD_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port D bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PE_OE + hex "GIO_PE_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port E bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PE_OUT + hex "GIO_PE_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port E bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PV_OE + hex "GIO_PV_OE" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the direction of virtual general port V bits. 1 is out, + 0 is in. This is often totally different depending on the product + used. These bits are used for all kinds of stuff. If you don't know + what to use, it is always safe to put all as inputs, although + floating inputs isn't good. + +config ETRAX_DEF_GIO_PV_OUT + hex "GIO_PV_OUT" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the initial data for the virtual general port V bits. + Most products should use 0000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-fs/Makefile b/arch/cris/arch-v32/mach-fs/Makefile new file mode 100644 index 00000000000..4ff407a1b93 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +bj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-fs/arbiter.c b/arch/cris/arch-v32/mach-fs/arbiter.c new file mode 100644 index 00000000000..84d31bd7b69 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/arbiter.c @@ -0,0 +1,404 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define NBR_OF_CLIENTS 14 +#define NBR_OF_SLOTS 64 +#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_REGIONS 2 + +static struct crisv32_watch_entry watches[NUMBER_OF_BP] = { + {regi_marb_bp0}, + {regi_marb_bp1}, + {regi_marb_bp2}, + {regi_marb_bp3} +}; + +static u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static u8 active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static int max_bandwidth[NBR_OF_REGIONS] = + { SDRAM_BANDWIDTH, INTMEM_BANDWIDTH }; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < NBR_OF_CLIENTS; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!active_clients[region][client] + || unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = + NBR_OF_SLOTS / requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!active_clients[region][client]) { + client = (client + 1) % NBR_OF_CLIENTS; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % NBR_OF_CLIENTS; + } + if (region == EXT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, + val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, + val[slot]); + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; + crisv32_arbiter_config(EXT_REGION, 0); + crisv32_arbiter_config(INT_REGION, 0); + + if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, + "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + arbiter_all_clients, arbiter_all_write, NULL); +#endif +} + +/* Main entry for bandwidth allocation. */ + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + + crisv32_arbiter_init(); + + for (i = 0; i < NBR_OF_CLIENTS; i++) { + total_assigned += requested_slots[region][i]; + total_clients += active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + active_clients[region][client] = 1; + requested_slots[region][client] = req; + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); + + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + + requested_slots[region][client] = 0; + active_clients[region][client] = 0; + + for (i = 0; i < NBR_OF_CLIENTS; i++) + total_assigned += requested_slots[region][i]; + + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[i].used) { + reg_marb_rw_intr_mask intr_mask = + REG_RD(marb, regi_marb, rw_intr_mask); + + watches[i].used = 1; + watches[i].start = start; + watches[i].end = start + size; + watches[i].cb = cb; + + REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, + watches[i].start); + REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, + watches[i].end); + REG_WR_INT(marb_bp, watches[i].instance, rw_op, + accesses); + REG_WR_INT(marb_bp, watches[i].instance, rw_clients, + clients); + + if (i == 0) + intr_mask.bp0 = regk_marb_yes; + else if (i == 1) + intr_mask.bp1 = regk_marb_yes; + else if (i == 2) + intr_mask.bp2 = regk_marb_yes; + else if (i == 3) + intr_mask.bp3 = regk_marb_yes; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + spin_unlock(&arbiter_lock); + + return i; + } + } + spin_unlock(&arbiter_lock); + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); + + if (id == 0) + intr_mask.bp0 = regk_marb_no; + else if (id == 1) + intr_mask.bp2 = regk_marb_no; + else if (id == 2) + intr_mask.bp2 = regk_marb_no; + else if (id == 3) + intr_mask.bp3 = regk_marb_no; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_r_masked_intr masked_intr = + REG_RD(marb, regi_marb, r_masked_intr); + reg_marb_bp_r_brk_clients r_clients; + reg_marb_bp_r_brk_addr r_addr; + reg_marb_bp_r_brk_op r_op; + reg_marb_bp_r_brk_first_client r_first; + reg_marb_bp_r_brk_size r_size; + reg_marb_bp_rw_ack ack = { 0 }; + reg_marb_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + + if (masked_intr.bp0) { + watch = &watches[0]; + ack_intr.bp0 = regk_marb_yes; + } else if (masked_intr.bp1) { + watch = &watches[1]; + ack_intr.bp1 = regk_marb_yes; + } else if (masked_intr.bp2) { + watch = &watches[2]; + ack_intr.bp2 = regk_marb_yes; + } else if (masked_intr.bp3) { + watch = &watches[3]; + ack_intr.bp3 = regk_marb_yes; + } else { + return IRQ_NONE; + } + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bp, watch->instance, r_brk_size); + + printk(KERN_INFO "Arbiter IRQ\n"); + printk(KERN_INFO "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); + + REG_WR(marb_bp, watch->instance, rw_ack, ack); + REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); + + printk(KERN_INFO "IRQ occured at %lX\n", get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} diff --git a/arch/cris/arch-v32/mach-fs/cpufreq.c b/arch/cris/arch-v32/mach-fs/cpufreq.c new file mode 100644 index 00000000000..d57631c0d8d --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/cpufreq.c @@ -0,0 +1,146 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <asm/arch/hwregs/reg_rdwr.h> +#include <asm/arch/hwregs/config_defs.h> +#include <asm/arch/hwregs/bif_core_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i; + struct cpufreq_freqs freqs; + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + + for_each_possible_cpu(i) { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target + (policy, cris_freq_table, target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_bif_core_rw_sdram_timing timing = + REG_RD(bif_core, regi_bif_core, rw_sdram_timing); + timing.cpd = (freqs->new == 200000 ? 0 : 1); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++) ; + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/kernel/dma.c b/arch/cris/arch-v32/mach-fs/dma.c index 570e19128ff..a6acf4e6345 100644 --- a/arch/cris/arch-v32/kernel/dma.c +++ b/arch/cris/arch-v32/mach-fs/dma.c @@ -3,49 +3,54 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <asm/dma.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/hwregs/config_defs.h> -#include <asm/arch/hwregs/strmux_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/config_defs.h> +#include <hwregs/strmux_defs.h> #include <linux/errno.h> #include <asm/system.h> -#include <asm/arch/arbiter.h> +#include <asm/arch/mach/arbiter.h> static char used_dma_channels[MAX_DMA_CHANNELS]; -static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; static DEFINE_SPINLOCK(dma_lock); -int crisv32_request_dma(unsigned int dmanr, const char * device_id, - unsigned options, unsigned int bandwidth, +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) { unsigned long flags; reg_config_rw_clk_ctrl clk_ctrl; reg_strmux_rw_cfg strmux_cfg; - if (crisv32_arbiter_allocate_bandwidth(dmanr, - options & DMA_INT_MEM ? INT_REGION : EXT_REGION, - bandwidth)) - return -ENOMEM; + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? + INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; spin_lock_irqsave(&dma_lock, flags); if (used_dma_channels[dmanr]) { spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); } if (options & DMA_PANIC_ON_ERROR) panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); return -EBUSY; } clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); - switch(dmanr) - { + switch (dmanr) { case 0: case 1: clk_ctrl.dma01_eth0 = 1; @@ -72,7 +77,9 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, default: spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS - 1); } if (options & DMA_PANIC_ON_ERROR) @@ -80,8 +87,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, return -EINVAL; } - switch(owner) - { + switch (owner) { case dma_eth0: if (dmanr == 0) strmux_cfg.dma0 = regk_strmux_eth0; @@ -212,7 +218,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, used_dma_channels_users[dmanr] = device_id; REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); - spin_unlock_irqrestore(&dma_lock,flags); + spin_unlock_irqrestore(&dma_lock, flags); return 0; } diff --git a/arch/cris/arch-v32/lib/dram_init.S b/arch/cris/arch-v32/mach-fs/dram_init.S index 218fbe259ee..6fbad336527 100644 --- a/arch/cris/arch-v32/lib/dram_init.S +++ b/arch/cris/arch-v32/mach-fs/dram_init.S @@ -1,23 +1,22 @@ -/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $ - * +/* * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * * Note: This file may not modify r8 or r9 because they are used to * carry information from the decompresser to the kernel * - * Copyright (C) 2000-2003 Axis Communications AB + * Copyright (C) 2000-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ /* Just to be certain the config file is included, we include it here - * explicitly instead of depending on it being included in the file that + * explicitely instead of depending on it being included in the file that * uses this code. */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> ;; WARNING! The registers r8 and r9 are used as parameters carrying ;; information from the decompressor (if the kernel was compressed). @@ -46,7 +45,7 @@ move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2 move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 - and.d 0x07, $r1 ; Get CAS latency + and.d 0x07, $r1 ; Get CAS latency cmpq 2, $r1 ; CL = 2 ? beq _bw_check nop @@ -80,12 +79,10 @@ _set_timing: subq 1, $r2 ; Issue initialization command sequence - move.d _sdram_commands_start, $r2 - and.d 0x000fffff, $r2 ; Make sure commands are read from flash - move.d _sdram_commands_end, $r3 - and.d 0x000fffff, $r3 + lapc _sdram_commands_start, $r2 + lapc _sdram_commands_end, $r3 1: clear.d $r6 - move.b [$r2+], $r6 ; Load command + move.b [$r2+], $r6 ; Load command or.d $r4, $r6 ; Add calculated mrs move.d $r6, [$r5] ; Write rw_sdram_cmd ; Wait 80 ns between each command diff --git a/arch/cris/arch-v32/lib/hw_settings.S b/arch/cris/arch-v32/mach-fs/hw_settings.S index fff9443513d..8bde93c3621 100644 --- a/arch/cris/arch-v32/lib/hw_settings.S +++ b/arch/cris/arch-v32/mach-fs/hw_settings.S @@ -1,18 +1,16 @@ /* - * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $ - * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. * Don't forget to update the tools if you change this table. * - * Copyright (C) 2001 Axis Communications AB + * Copyright (C) 2001-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> -#include <asm/arch/hwregs/asm/gio_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> .ascii "HW_PARAM_MAGIC" ; Magic number .dword 0xc0004000 ; Kernel start address diff --git a/arch/cris/arch-v32/mach-fs/io.c b/arch/cris/arch-v32/mach-fs/io.c new file mode 100644 index 00000000000..a03a3ad3a18 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/io.c @@ -0,0 +1,191 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/hwregs/gio_defs.h> + +#ifndef DEBUG +#define DEBUG(x) +#endif + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 8 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pd_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pe_din), + 18 + } +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led_net1_green; +struct crisv32_iopin crisv32_led_net1_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 18 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += + crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += + crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + +#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO + ret += + crisv32_io_get_name(&crisv32_led_net1_green, + CONFIG_ETRAX_LED_G_NET1); + crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) { + crisv32_io_get_name(&crisv32_led_net1_red, + CONFIG_ETRAX_LED_R_NET1); + crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out); + } else + crisv32_led_net1_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + DEBUG(printk(KERN_DEBUG "crisv32_io_get: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + + DEBUG(printk(KERN_DEBUG + "crisv32_io_get_name: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif diff --git a/arch/cris/arch-v32/mach-fs/pinmux.c b/arch/cris/arch-v32/mach-fs/pinmux.c new file mode 100644 index 00000000000..d722ad9ae62 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/pinmux.c @@ -0,0 +1,309 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> + +#undef DEBUG + +#define PORT_PINS 18 +#define PORTS 4 + +static char pins[PORTS][PORT_PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = + pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; + REG_WR(pinmux, regi_pinmux, rw_pa, pa); + crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port][i] != pinmux_none) + && (pins[port][i] != pinmux_gpio) + && (pins[port][i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_sser0: + ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.sser0 = regk_pinmux_yes; + break; + case pinmux_sser1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + hwprot.sser1 = regk_pinmux_yes; + break; + case pinmux_ata0: + ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); + hwprot.ata0 = regk_pinmux_yes; + break; + case pinmux_ata1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); + hwprot.ata1 = regk_pinmux_yes; + break; + case pinmux_ata2: + ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata3: + ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata: + ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); + hwprot.ata = regk_pinmux_yes; + break; + case pinmux_eth1: + ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); + hwprot.eth1 = regk_pinmux_yes; + hwprot.eth1_mgm = regk_pinmux_yes; + break; + case pinmux_timer: + ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.timer = regk_pinmux_yes; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + + for (i = 0; i < PORT_PINS; i++) { + if (pins[port][i] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[port][i] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_C, 4, 7); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 11); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_C, 12, 15); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_sser0: + ret = crisv32_pinmux_dealloc(PORT_C, 0, 3); + ret |= crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.sser0 = regk_pinmux_no; + break; + case pinmux_sser1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + hwprot.sser1 = regk_pinmux_no; + break; + case pinmux_ata0: + ret = crisv32_pinmux_dealloc(PORT_D, 5, 7); + ret |= crisv32_pinmux_dealloc(PORT_D, 15, 17); + hwprot.ata0 = regk_pinmux_no; + break; + case pinmux_ata1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + ret |= crisv32_pinmux_dealloc(PORT_E, 17, 17); + hwprot.ata1 = regk_pinmux_no; + break; + case pinmux_ata2: + ret = crisv32_pinmux_dealloc(PORT_C, 11, 15); + ret |= crisv32_pinmux_dealloc(PORT_E, 3, 3); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata3: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 10); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 2); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata: + ret = crisv32_pinmux_dealloc(PORT_B, 0, 15); + ret |= crisv32_pinmux_dealloc(PORT_D, 8, 15); + hwprot.ata = regk_pinmux_no; + break; + case pinmux_eth1: + ret = crisv32_pinmux_dealloc(PORT_E, 0, 17); + hwprot.eth1 = regk_pinmux_no; + hwprot.eth1_mgm = regk_pinmux_no; + break; + case pinmux_timer: + ret = crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.timer = regk_pinmux_no; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_dump(void) +{ + int i, j; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + printk(KERN_DEBUG "Port %c\n", 'B' + i); + for (j = 0; j < PORT_PINS; j++) + printk(KERN_DEBUG " Pin %d = %d\n", j, pins[i][j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-fs/vcs_hook.c b/arch/cris/arch-v32/mach-fs/vcs_hook.c new file mode 100644 index 00000000000..593b10f07ef --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.c @@ -0,0 +1,100 @@ +/* + * Call simulator hook. This is the part running in the + * simulated program. + */ + +#include "vcs_hook.h" +#include <stdarg.h> +#include <asm/arch-v32/hwregs/reg_map.h> +#include <asm/arch-v32/hwregs/intr_vect_defs.h> + +#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ +#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ + +#define HOOK_DATA(offset) ((unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define VHOOK_DATA(offset) ((volatile unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)HOOK_MEM_BASE_ADDR)[offset] + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + unsigned i; + unsigned ret; +#ifdef USING_SOS + PREEMPT_OFF_SAVE(); +#endif + + /* pass parameters */ + HOOK_DATA(0) = id; + + /* Have to make hook_print_str a special case since we call with a + * parameter of byte type. Should perhaps be a separate + * hook_call. */ + + if (id == hook_print_str) { + int i; + char *str; + + HOOK_DATA(1) = pcnt; + + va_start(ap, pcnt); + str = (char *)va_arg(ap, unsigned); + + for (i = 0; i != pcnt; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + + HOOK_DATA_BYTE(8 + i) = 0; /* null byte */ + } else { + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + } + + /* read from mem to make sure data has propagated to memory before + * trigging */ + ret = *((volatile unsigned *)HOOK_MEM_BASE_ADDR); + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + +#ifdef USING_SOS + PREEMPT_RESTORE(); +#endif + return ret; +} + +unsigned hook_buf(unsigned i) +{ + return (HOOK_DATA(i)); +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_KICK_DOG(void) +{ + (void)hook_call(hook_kick_dog, 0); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ + (void)hook_call(hook_dog_timeout, 1, t); +} + diff --git a/arch/cris/arch-v32/kernel/vcs_hook.h b/arch/cris/arch-v32/mach-fs/vcs_hook.h index 7d73709e3cc..c000b9fece4 100644 --- a/arch/cris/arch-v32/kernel/vcs_hook.h +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.h @@ -1,11 +1,11 @@ -// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook functions +/* + * Call simulator hook functions + */ #ifndef HOOK_H #define HOOK_H -int hook_call( unsigned id, unsigned pcnt, ...); +int hook_call(unsigned id, unsigned pcnt, ...); enum hook_ids { hook_debug_on = 1, diff --git a/arch/cris/arch-v32/mm/Makefile b/arch/cris/arch-v32/mm/Makefile index 9146f88484b..0b801f2964a 100644 --- a/arch/cris/arch-v32/mm/Makefile +++ b/arch/cris/arch-v32/mm/Makefile @@ -1,3 +1,4 @@ # Makefile for the Linux/cris parts of the memory manager. -obj-y := mmu.o init.o tlb.o intmem.o +obj-y += mmu.o init.o tlb.o intmem.o +obj-$(CONFIG_ETRAX_L2CACHE) += l2cache.o diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c index a84ba7ff22d..5a9ac583464 100644 --- a/arch/cris/arch-v32/mm/init.c +++ b/arch/cris/arch-v32/mm/init.c @@ -65,7 +65,7 @@ cris_mmu_init(void) REG_STATE(mmu, rw_mm_cfg, seg_d, page) | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_STATE(mmu, rw_mm_cfg, seg_a, page) | #else REG_STATE(mmu, rw_mm_cfg, seg_a, linear) | @@ -84,13 +84,9 @@ cris_mmu_init(void) mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) | REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) | REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) | -#ifndef CONFIG_ETRAXFS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) | -#else - REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) | -#endif REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) | #else REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) | diff --git a/arch/cris/arch-v32/mm/intmem.c b/arch/cris/arch-v32/mm/intmem.c index 41ee7f7997f..9e8b69cdf19 100644 --- a/arch/cris/arch-v32/mm/intmem.c +++ b/arch/cris/arch-v32/mm/intmem.c @@ -7,11 +7,17 @@ #include <linux/list.h> #include <linux/slab.h> #include <asm/io.h> -#include <asm/arch/memmap.h> +#include <memmap.h> #define STATUS_FREE 0 #define STATUS_ALLOCATED 1 +#ifdef CONFIG_ETRAX_L2CACHE +#define RESERVED_SIZE 66*1024 +#else +#define RESERVED_SIZE 0 +#endif + struct intmem_allocation { struct list_head entry; unsigned int size; @@ -30,9 +36,10 @@ static void crisv32_intmem_init(void) struct intmem_allocation* alloc = (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL); INIT_LIST_HEAD(&intmem_allocations); - intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE); + intmem_virtual = ioremap(MEM_INTMEM_START + RESERVED_SIZE, + MEM_INTMEM_SIZE - RESERVED_SIZE); initiated = 1; - alloc->size = MEM_INTMEM_SIZE; + alloc->size = MEM_INTMEM_SIZE - RESERVED_SIZE; alloc->offset = 0; alloc->status = STATUS_FREE; list_add_tail(&alloc->entry, &intmem_allocations); @@ -59,19 +66,23 @@ void* crisv32_intmem_alloc(unsigned size, unsigned align) (struct intmem_allocation*) kmalloc(sizeof *alloc, GFP_ATOMIC); alloc->status = STATUS_FREE; - alloc->size = allocation->size - size - alignment; - alloc->offset = allocation->offset + size; + alloc->size = allocation->size - size - + alignment; + alloc->offset = allocation->offset + size + + alignment; list_add(&alloc->entry, &allocation->entry); if (alignment) { - struct intmem_allocation* tmp; - tmp = (struct intmem_allocation*) - kmalloc(sizeof *tmp, GFP_ATOMIC); + struct intmem_allocation *tmp; + tmp = (struct intmem_allocation *) + kmalloc(sizeof *tmp, + GFP_ATOMIC); tmp->offset = allocation->offset; tmp->size = alignment; tmp->status = STATUS_FREE; allocation->offset += alignment; - list_add_tail(&tmp->entry, &allocation->entry); + list_add_tail(&tmp->entry, + &allocation->entry); } } allocation->status = STATUS_ALLOCATED; @@ -96,22 +107,24 @@ void crisv32_intmem_free(void* addr) list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { if (allocation->offset == (int)(addr - intmem_virtual)) { - struct intmem_allocation* prev = + struct intmem_allocation *prev = list_entry(allocation->entry.prev, struct intmem_allocation, entry); - struct intmem_allocation* next = + struct intmem_allocation *next = list_entry(allocation->entry.next, struct intmem_allocation, entry); allocation->status = STATUS_FREE; /* Join with prev and/or next if also free */ - if (prev->status == STATUS_FREE) { + if ((prev != &intmem_allocations) && + (prev->status == STATUS_FREE)) { prev->size += allocation->size; list_del(&allocation->entry); kfree(allocation); allocation = prev; } - if (next->status == STATUS_FREE) { + if ((next != &intmem_allocations) && + (next->status == STATUS_FREE)) { allocation->size += next->size; list_del(&next->entry); kfree(next); @@ -125,15 +138,16 @@ void crisv32_intmem_free(void* addr) void* crisv32_intmem_phys_to_virt(unsigned long addr) { - return (void*)(addr - MEM_INTMEM_START+ - (unsigned long)intmem_virtual); + return (void *)(addr - (MEM_INTMEM_START + RESERVED_SIZE) + + (unsigned long)intmem_virtual); } unsigned long crisv32_intmem_virt_to_phys(void* addr) { return (unsigned long)((unsigned long )addr - - (unsigned long)intmem_virtual + MEM_INTMEM_START); + (unsigned long)intmem_virtual + MEM_INTMEM_START + + RESERVED_SIZE); } - +module_init(crisv32_intmem_init); diff --git a/arch/cris/arch-v32/mm/l2cache.c b/arch/cris/arch-v32/mm/l2cache.c new file mode 100644 index 00000000000..332ff10dcc6 --- /dev/null +++ b/arch/cris/arch-v32/mm/l2cache.c @@ -0,0 +1,29 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/l2cache_defs.h> +#include <asm/io.h> + +#define L2CACHE_SIZE 64 + +int __init l2cache_init(void) +{ + reg_l2cache_rw_ctrl ctrl = {0}; + reg_l2cache_rw_cfg cfg = {.en = regk_l2cache_yes}; + + ctrl.csize = L2CACHE_SIZE; + ctrl.cbase = L2CACHE_SIZE / 4 + (L2CACHE_SIZE % 4 ? 1 : 0); + REG_WR(l2cache, regi_l2cache, rw_ctrl, ctrl); + + /* Flush the tag memory */ + memset((void *)(MEM_INTMEM_START | MEM_NON_CACHEABLE), 0, 2*1024); + + /* Enable the cache */ + REG_WR(l2cache, regi_l2cache, rw_cfg, cfg); + + return 0; +} + diff --git a/arch/cris/arch-v32/mm/mmu.S b/arch/cris/arch-v32/mm/mmu.S index 27b70e5006a..2238d154bde 100644 --- a/arch/cris/arch-v32/mm/mmu.S +++ b/arch/cris/arch-v32/mm/mmu.S @@ -1,3 +1,5 @@ +; WARNING : The refill handler has been modified, see below !!! + /* * Copyright (C) 2003 Axis Communications AB * @@ -61,6 +63,14 @@ ; Note that the code is optimized to minimize stalls (makes the code harder ; to read). ; +; WARNING !!! +; Modified by Mikael Asker 060725: added a workaround for strange TLB +; behavior. If the same PTE is present in more than one set, the TLB +; doesn't recognize it and we get stuck in a loop of refill exceptions. +; The workaround detects such loops and exits them by flushing +; the TLB contents. The problem and workaround were verified +; in VCS by Mikael Starvik. +; ; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each ; PMD holds 16 MB of virtual memory. ; Bits 0-12 : Offset within a page @@ -68,6 +78,11 @@ ; Bits 24-31 : PMD offset within the PGD .macro MMU_REFILL_HANDLER handler, mmu + .data +1: .dword 0 ; refill_count + ; == 0 <=> last_refill_cause is invalid +2: .dword 0 ; last_refill_cause + .text .globl \handler \handler: subq 4, $sp @@ -76,42 +91,96 @@ subq 4, $sp move \mmu, $srs ; Select MMU support register bank move.d $acr, [$sp] - subq 4, $sp - move.d $r0, [$sp] + subq 12, $sp + move.d 1b, $acr ; Point to refill_count + movem $r2, [$sp] + + test.d [$acr] ; refill_count == 0 ? + beq 5f ; yes, last_refill_cause is invalid + move.d $acr, $r1 + + ; last_refill_cause is valid, investigate cause + addq 4, $r1 ; Point to last_refill_cause + move $s3, $r0 ; Get rw_mm_cause + move.d [$r1], $r2 ; Get last_refill_cause + cmp.d $r0, $r2 ; rw_mm_cause == last_refill_cause ? + beq 6f ; yes, increment count + moveq 1, $r2 + + ; rw_mm_cause != last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move.d $r0, [$r1] ; last_refill_cause = rw_mm_cause + +3: ; Probably not in a loop, continue normal processing #ifdef CONFIG_SMP move $s7, $acr ; PGD #else move.d per_cpu__current_pgd, $acr ; PGD #endif ; Look up PMD in PGD - move $s3, $r0 ; rw_mm_cause lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31) move.d [$acr], $acr ; PGD for the current process addi $r0.d, $acr, $acr move $s3, $r0 ; rw_mm_cause move.d [$acr], $acr ; Get PMD - beq 1f + beq 8f ; Look up PTE in PMD lsrq PAGE_SHIFT, $r0 and.w PAGE_MASK, $acr ; Remove PMD flags and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23) addi $r0.d, $acr, $acr move.d [$acr], $acr ; Get PTE - beq 2f - move.d [$sp+], $r0 ; Pop r0 in delayslot + beq 9f + movem [$sp], $r2 ; Restore r0-r2 in delay slot + addq 12, $sp ; Store in TLB move $acr, $s5 - ; Return +4: ; Return move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp rete rfe -1: ; PMD missing, let the mm subsystem fix it up. - move.d [$sp+], $r0 ; Pop r0 -2: ; PTE missing, let the mm subsystem fix it up. + +5: ; last_refill_cause is invalid + moveq 1, $r2 + addq 4, $r1 ; Point to last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move $s3, $r0 ; Get rw_mm_cause + ba 3b ; Continue normal processing + move.d $r0,[$r1] ; last_refill_cause = rw_mm_cause + +6: ; rw_mm_cause == last_refill_cause + move.d [$acr], $r2 ; Get refill_count + cmpq 4, $r2 ; refill_count > 4 ? + bhi 7f ; yes + addq 1, $r2 ; refill_count++ + ba 3b ; Continue normal processing + move.d $r2, [$acr] + +7: ; refill_count > 4, error + move.d $acr, $r0 ; Save pointer to refill_count + clear.d [$r0] ; refill_count = 0 + + ;; rewind the short stack + movem [$sp], $r2 ; Restore r0-r2 + addq 12, $sp + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + ;; Keep it simple (slow), save all the regs. + SAVE_ALL + jsr __flush_tlb_all + nop + ba ret_from_intr ; Return + nop + +8: ; PMD missing, let the mm subsystem fix it up. + movem [$sp], $r2 ; Restore r0-r2 +9: ; PTE missing, let the mm subsystem fix it up. + addq 12, $sp move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp SAVE_ALL move \mmu, $srs diff --git a/arch/cris/arch-v32/mm/tlb.c b/arch/cris/arch-v32/mm/tlb.c index a076ef6e938..eda5ebcaea5 100644 --- a/arch/cris/arch-v32/mm/tlb.c +++ b/arch/cris/arch-v32/mm/tlb.c @@ -13,8 +13,8 @@ #include <asm/arch/hwregs/supp_reg.h> #define UPDATE_TLB_SEL_IDX(val) \ -do { \ - unsigned long tlb_sel; \ +do { \ + unsigned long tlb_sel; \ \ tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, val); \ SUPP_REG_WR(RW_MM_TLB_SEL, tlb_sel); \ @@ -30,8 +30,8 @@ do { \ * The TLB can host up to 256 different mm contexts at the same time. The running * context is found in the PID register. Each TLB entry contains a page_id that * has to match the PID register to give a hit. page_id_map keeps track of which - * mm is assigned to which page_id, making sure it's known when to invalidate TLB - * entries. + * mm's is assigned to which page_id's, making sure it's known when to + * invalidate TLB entries. * * The last page_id is never running, it is used as an invalid page_id so that * it's possible to make TLB entries that will nerver match. @@ -179,29 +179,29 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - int cpu = smp_processor_id(); - - /* Make sure there is a MMU context. */ - spin_lock(&mmu_context_lock); - get_mmu_context(next); - cpu_set(cpu, next->cpu_vm_mask); - spin_unlock(&mmu_context_lock); - - /* - * Remember the pgd for the fault handlers. Keep a separate copy of it - * because current and active_mm might be invalid at points where - * there's still a need to derefer the pgd. - */ - per_cpu(current_pgd, cpu) = next->pgd; - - /* Switch context in the MMU. */ - if (tsk && task_thread_info(tsk)) - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls); - } - else - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); - } + if (prev != next) { + int cpu = smp_processor_id(); + + /* Make sure there is a MMU context. */ + spin_lock(&mmu_context_lock); + get_mmu_context(next); + cpu_set(cpu, next->cpu_vm_mask); + spin_unlock(&mmu_context_lock); + + /* + * Remember the pgd for the fault handlers. Keep a seperate + * copy of it because current and active_mm might be invalid + * at points where * there's still a need to derefer the pgd. + */ + per_cpu(current_pgd, cpu) = next->pgd; + + /* Switch context in the MMU. */ + if (tsk && task_thread_info(tsk)) { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | + task_thread_info(tsk)->tls); + } else { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); + } + } } diff --git a/arch/cris/arch-v32/vmlinux.lds.S b/arch/cris/arch-v32/vmlinux.lds.S index fead8c59ea6..d5f28e40717 100644 --- a/arch/cris/arch-v32/vmlinux.lds.S +++ b/arch/cris/arch-v32/vmlinux.lds.S @@ -9,6 +9,13 @@ */ #include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> + +#ifdef CONFIG_ETRAX_VMEM_SIZE +#define __CONFIG_ETRAX_VMEM_SIZE CONFIG_ETRAX_VMEM_SIZE +#else +#define __CONFIG_ETRAX_VMEM_SIZE 0 +#endif jiffies = jiffies_64; SECTIONS @@ -17,18 +24,19 @@ SECTIONS dram_start = .; ebp_start = .; - /* The boot section is only necessary until the VCS top level testbench */ - /* includes both flash and DRAM. */ + /* The boot section is only necessary until the VCS top */ + /* level testbench includes both flash and DRAM. */ .boot : { *(.boot) } - . = DRAM_VIRTUAL_BASE + 0x4000; /* See head.S and pages reserved at the start. */ + /* See head.S and pages reserved at the start. */ + . = DRAM_VIRTUAL_BASE + 0x4000; _text = .; /* Text and read-only data. */ text_start = .; /* Lots of aliases. */ _stext = .; __stext = .; .text : { - *(.text) + TEXT_TEXT SCHED_TEXT LOCK_TEXT *(.fixup) @@ -39,9 +47,9 @@ SECTIONS __etext = .; . = ALIGN(4); /* Exception table. */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; RODATA @@ -54,33 +62,27 @@ SECTIONS __edata = . ; /* End of data section. */ _edata = . ; - . = ALIGN(8192); /* init_task and stack, must be aligned. */ - .data.init_task : { *(.data.init_task) } + . = ALIGN(PAGE_SIZE); /* init_task and stack, must be aligned. */ + .data.init_task : { *(.data.init_task) } - . = ALIGN(8192); /* Init code and data. */ - __init_begin = .; + . = ALIGN(PAGE_SIZE); /* Init code and data. */ + __init_begin = .; .init.text : { _sinittext = .; INIT_TEXT _einittext = .; } .init.data : { INIT_DATA } - . = ALIGN(16); - __setup_start = .; - .init.setup : { *(.init.setup) } - __setup_end = .; + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; __start___param = .; __param : { *(__param) } __stop___param = .; - .initcall.init : { + .initcall.init : { __initcall_start = .; - *(.initcall1.init); - *(.initcall2.init); - *(.initcall3.init); - *(.initcall4.init); - *(.initcall5.init); - *(.initcall6.init); - *(.initcall7.init); + INITCALLS __initcall_end = .; } @@ -91,25 +93,23 @@ SECTIONS } SECURITY_INIT - PERCPU(8192) + __vmlinux_end = .; /* Last address of the physical file. */ + PERCPU(PAGE_SIZE) -#ifdef CONFIG_BLK_DEV_INITRD .init.ramfs : { __initramfs_start = .; *(.init.ramfs) __initramfs_end = .; - /* - * We fill to the next page, so we can discard all init - * pages without needing to consider what payload might be - * appended to the kernel image. - */ - FILL (0); - . = ALIGN (8192); } -#endif - __vmlinux_end = .; /* Last address of the physical file. */ - __init_end = .; + /* + * We fill to the next page, so we can discard all init + * pages without needing to consider what payload might be + * appended to the kernel image. + */ + . = ALIGN (PAGE_SIZE); + + __init_end = .; __data_end = . ; /* Move to _edata? */ __bss_start = .; /* BSS. */ @@ -123,11 +123,11 @@ SECTIONS __end = .; /* Sections to be discarded */ - /DISCARD/ : { + /DISCARD/ : { EXIT_TEXT EXIT_DATA *(.exitcall.exit) } - dram_end = dram_start + CONFIG_ETRAX_DRAM_SIZE*1024*1024; + dram_end = dram_start + (CONFIG_ETRAX_DRAM_SIZE - __CONFIG_ETRAX_VMEM_SIZE)*1024*1024; } diff --git a/arch/cris/artpec_3_defconfig b/arch/cris/artpec_3_defconfig new file mode 100644 index 00000000000..41fe67409aa --- /dev/null +++ b/arch/cris/artpec_3_defconfig @@ -0,0 +1,582 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc3 +# Mon Dec 3 11:18:54 2007 +# +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 +CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# General setup +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_ETRAX_FAST_TIMER=y +# CONFIG_ETRAX_KMALLOCED_MODULES is not set +# CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y + +# +# Hardware setup +# +# CONFIG_ETRAX100LX is not set +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +# CONFIG_ETRAXFS is not set +CONFIG_CRIS_MACH_ARTPEC3=y +# CONFIG_ETRAX_VCS_SIM is not set +# CONFIG_ETRAX_ARCH_V10 is not set +CONFIG_ETRAX_ARCH_V32=y +CONFIG_ETRAX_DRAM_SIZE=32 +CONFIG_ETRAX_VMEM_SIZE=8 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 +CONFIG_ETRAX_FLASH1_SIZE=4 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +# CONFIG_ETRAX_DEBUG_PORT_NULL is not set +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 +CONFIG_ETRAX_SERIAL_PORTS=5 +CONFIG_ETRAX_DEF_GIO_PA_OE=1c +CONFIG_ETRAX_DEF_GIO_PA_OUT=00 +CONFIG_ETRAX_DEF_GIO_PB_OE=00000 +CONFIG_ETRAX_DEF_GIO_PB_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PC_OE=00000 +CONFIG_ETRAX_DEF_GIO_PC_OUT=00000 + +# +# Artpec-3 options +# +CONFIG_ETRAX_L2CACHE=y +CONFIG_ETRAX_DDR=y +CONFIG_ETRAX_DDR2_MRS=0 +CONFIG_ETRAX_DDR2_TIMING=0 +CONFIG_ETRAX_DDR2_CONFIG=0 +CONFIG_ETRAX_PIO_CE0_CFG=0 +CONFIG_ETRAX_PIO_CE1_CFG=0 +CONFIG_ETRAX_PIO_CE2_CFG=0 +# CONFIG_CPU_FREQ is not set +# CONFIG_ETRAX_NBR_LED_GRP_ZERO is not set +CONFIG_ETRAX_NBR_LED_GRP_ONE=y +# CONFIG_ETRAX_NBR_LED_GRP_TWO is not set +CONFIG_ETRAX_LED_G_NET0="PA3" +CONFIG_ETRAX_LED_R_NET0="PA4" +CONFIG_ETRAX_V32_LED2G="PA5" +CONFIG_ETRAX_V32_LED2R="PA6" +CONFIG_ETRAX_V32_LED3G="PA7" +CONFIG_ETRAX_V32_LED3R="PA7" + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Drivers for built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +# CONFIG_ETRAX_I2C is not set +# CONFIG_ETRAX_GPIO is not set +# CONFIG_ETRAX_NO_PHY is not set +# CONFIG_ETRAX_ETHERNET_IFACE0 is not set +# CONFIG_ETRAX_ETHERNET_GBIT is not set +# CONFIG_ETRAXFS_SERIAL is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE is not set +# CONFIG_ETRAX_NANDFLASH is not set +# CONFIG_ETRAX_CARDBUS is not set +# CONFIG_ETRAX_IOP_FW_LOAD is not set +# CONFIG_ETRAX_STREAMCOPROC is not set +# CONFIG_ETRAX_SPI_MMC is not set +# CONFIG_ETRAX_SPI_MMC_BOARD is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_RAM=y +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=y +CONFIG_MTDRAM_TOTAL_SIZE=0 +CONFIG_MTDRAM_ERASE_SIZE=64 +CONFIG_MTDRAM_ABS_POS=0x0 +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_HW_RANDOM=y +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PROFILING is not set +# CONFIG_SYSTEM_PROFILER is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/defconfig b/arch/cris/defconfig index 9c33ae65993..59f36a58f84 100644 --- a/arch/cris/defconfig +++ b/arch/cris/defconfig @@ -1,52 +1,91 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.11 -# Mon Jun 20 13:42:02 2005 +# Linux kernel version: 2.6.24-rc3 +# Mon Dec 3 11:34:27 2007 # CONFIG_MMU=y -CONFIG_UID16=y +CONFIG_ZONE_DMA=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" # -# Code maturity level options +# General setup # CONFIG_EXPERIMENTAL=y -CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y - -# -# General setup -# +CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y # CONFIG_SWAP is not set # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set -CONFIG_SYSCTL=y +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set # CONFIG_AUDIT is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y # CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y CONFIG_FUTEX=y +CONFIG_ANON_INODES=y CONFIG_EPOLL=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y CONFIG_SHMEM=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set # -# Loadable module support +# IO Schedulers # -# CONFIG_MODULES is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" # # General setup @@ -54,12 +93,27 @@ CONFIG_CC_ALIGN_JUMPS=0 CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set CONFIG_GENERIC_HARDIRQS=y -# CONFIG_SMP is not set CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" # CONFIG_ETRAX_WATCHDOG is not set CONFIG_ETRAX_FAST_TIMER=y -# CONFIG_PREEMPT is not set +# CONFIG_ETRAX_KMALLOCED_MODULES is not set # CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y # # Hardware setup @@ -68,127 +122,180 @@ CONFIG_ETRAX_FAST_TIMER=y CONFIG_ETRAX100LX_V2=y # CONFIG_SVINTO_SIM is not set # CONFIG_ETRAXFS is not set -# CONFIG_ETRAXFS_SIM is not set +# CONFIG_CRIS_MACH_ARTPEC3 is not set +# CONFIG_ETRAX_VCS_SIM is not set CONFIG_ETRAX_ARCH_V10=y # CONFIG_ETRAX_ARCH_V32 is not set CONFIG_ETRAX_DRAM_SIZE=32 CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 CONFIG_ETRAX_FLASH1_SIZE=4 +# CONFIG_ETRAX_DEBUG_PORT0 is not set +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +CONFIG_ETRAX_DEBUG_PORT_NULL=y + +# +# CRIS v10 options +# CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 CONFIG_ETRAX_PA_LEDS=y # CONFIG_ETRAX_PB_LEDS is not set # CONFIG_ETRAX_CSP0_LEDS is not set # CONFIG_ETRAX_NO_LEDS is not set CONFIG_ETRAX_LED1G=2 -CONFIG_ETRAX_LED1R=2 -CONFIG_ETRAX_LED2G=3 -CONFIG_ETRAX_LED2R=3 +CONFIG_ETRAX_LED1R=3 +CONFIG_ETRAX_LED2G=4 +CONFIG_ETRAX_LED2R=5 CONFIG_ETRAX_LED3G=2 CONFIG_ETRAX_LED3R=2 -CONFIG_ETRAX_DEBUG_PORT0=y -# CONFIG_ETRAX_DEBUG_PORT1 is not set -# CONFIG_ETRAX_DEBUG_PORT2 is not set -# CONFIG_ETRAX_DEBUG_PORT3 is not set -# CONFIG_ETRAX_DEBUG_PORT_NULL is not set CONFIG_ETRAX_RESCUE_SER0=y # CONFIG_ETRAX_RESCUE_SER1 is not set # CONFIG_ETRAX_RESCUE_SER2 is not set # CONFIG_ETRAX_RESCUE_SER3 is not set -CONFIG_ETRAX_DEF_R_WAITSTATES=0x95a6 -CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x4 -CONFIG_ETRAX_SDRAM=y -CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x09e05757 -CONFIG_ETRAX_DEF_R_SDRAM_TIMING=0x80008002 -CONFIG_ETRAX_DEF_R_PORT_PA_DIR=0x1d -CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0x00 -CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=0x00 -CONFIG_ETRAX_DEF_R_PORT_PB_DIR=0x1e -CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0xf3 +CONFIG_ETRAX_DEF_R_WAITSTATES=95a6 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=104 +# CONFIG_ETRAX_SDRAM is not set +CONFIG_ETRAX_DEF_R_DRAM_CONFIG=1a200040 +CONFIG_ETRAX_DEF_R_DRAM_TIMING=5611 +CONFIG_ETRAX_DEF_R_PORT_PA_DIR=1c +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=00 +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DIR=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DATA=ff # CONFIG_ETRAX_SOFT_SHUTDOWN is not set # +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# # Drivers for built-in interfaces # CONFIG_ETRAX_ETHERNET=y -# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set -CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y CONFIG_ETRAX_SERIAL=y # CONFIG_ETRAX_SERIAL_FAST_TIMER is not set # CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is not set CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS=5 -CONFIG_ETRAX_SERIAL_PORT0=y -# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT is not set -CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y -# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN is not set -CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y -CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE=y -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA is not set -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED is not set -CONFIG_ETRAX_SER0_DTR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_RI_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_DSR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_CD_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_DTR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_RI_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_DSR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_CD_ON_PB_BIT=-1 +# CONFIG_ETRAX_SERIAL_PORT0 is not set # CONFIG_ETRAX_SERIAL_PORT1 is not set -CONFIG_ETRAX_SERIAL_PORT2=y -# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT is not set -CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT=y -# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN is not set -CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN=y -CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE=y -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA is not set -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PB is not set -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED is not set -CONFIG_ETRAX_SER2_DTR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_RI_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_DSR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_CD_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_DTR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_RI_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_DSR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_CD_ON_PB_BIT=-1 +# CONFIG_ETRAX_SERIAL_PORT2 is not set # CONFIG_ETRAX_SERIAL_PORT3 is not set -CONFIG_ETRAX_RS485=y -# CONFIG_ETRAX_RS485_ON_PA is not set -# CONFIG_ETRAX_RS485_DISABLE_RECEIVER is not set -CONFIG_ETRAX_IDE=y -CONFIG_ETRAX_IDE_DELAY=15 -CONFIG_ETRAX_IDE_PB7_RESET=y -# CONFIG_ETRAX_IDE_G27_RESET is not set -CONFIG_ETRAX_USB_HOST=y -CONFIG_ETRAX_USB_HOST_PORT1=y -CONFIG_ETRAX_USB_HOST_PORT2=y +# CONFIG_ETRAX_RS485 is not set +# CONFIG_ETRAX_IDE is not set +# CONFIG_ETRAX_USB_HOST is not set CONFIG_ETRAX_AXISFLASHMAP=y CONFIG_ETRAX_PTABLE_SECTOR=65536 # CONFIG_ETRAX_I2C is not set # CONFIG_ETRAX_GPIO is not set -CONFIG_ETRAX_RTC=y -CONFIG_ETRAX_DS1302=y -# CONFIG_ETRAX_PCF8563 is not set -CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT=y -CONFIG_ETRAX_DS1302_RSTBIT=0 -CONFIG_ETRAX_DS1302_SCLBIT=1 -CONFIG_ETRAX_DS1302_SDABIT=0 -CONFIG_ETRAX_DS1302_TRICKLE_CHARGE=0 +# CONFIG_ETRAX_RTC is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y # # Generic Driver Options # CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set - -# -# Memory Technology Devices (MTD) -# +# CONFIG_SYS_HYPERVISOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set # CONFIG_MTD_CMDLINE_PARTS is not set @@ -196,16 +303,20 @@ CONFIG_MTD_CONCAT=y # User Modules And Translation Layers # CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set # # RAM/ROM/Flash chip drivers # CONFIG_MTD_CFI=y -# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_JEDECPROBE=y CONFIG_MTD_GEN_PROBE=y # CONFIG_MTD_CFI_ADV_OPTIONS is not set CONFIG_MTD_MAP_BANK_WIDTH_1=y @@ -220,20 +331,18 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_CFI_INTELEXT is not set CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_CFI_AMDSTD_RETRY=0 # CONFIG_MTD_CFI_STAA is not set CONFIG_MTD_CFI_UTIL=y CONFIG_MTD_RAM=y # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set -# CONFIG_MTD_SHARP is not set -# CONFIG_MTD_JEDEC is not set # # Mapping drivers for chip access # CONFIG_MTD_COMPLEX_MAPPINGS=y # CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers @@ -244,7 +353,6 @@ CONFIG_MTD_MTDRAM=y CONFIG_MTDRAM_TOTAL_SIZE=0 CONFIG_MTDRAM_ERASE_SIZE=64 CONFIG_MTDRAM_ABS_POS=0x0 -# CONFIG_MTD_BLKMTD is not set # CONFIG_MTD_BLOCK2MTD is not set # @@ -253,216 +361,54 @@ CONFIG_MTDRAM_ABS_POS=0x0 # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# # CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set # -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Plug and Play support +# UBI - Unsorted block images # - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set # CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 -# CONFIG_BLK_DEV_INITRD is not set -CONFIG_INITRAMFS_SOURCE="" +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 # CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -CONFIG_IOSCHED_CFQ=y # CONFIG_ATA_OVER_ETH is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set -# CONFIG_PARIDE is not set - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# -# CONFIG_BLK_DEV_IDE_SATA is not set -# CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_IDE_TASK_IOCTL is not set - -# -# IDE chipset support/bugfixes -# -# CONFIG_IDE_GENERIC is not set -# CONFIG_IDE_ARM is not set -# CONFIG_IDEDMA_AUTO is not set -# CONFIG_BLK_DEV_HD is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set -# CONFIG_ISCSI_TCP is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Networking support -# -CONFIG_NET=y - -# -# Networking options -# -CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set -# CONFIG_NETLINK_DEV is not set -CONFIG_UNIX=y -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_IP_PNP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_TUNNEL is not set -CONFIG_IP_TCPDIAG=y -# CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# IP: Virtual Server Configuration -# -# CONFIG_IP_VS is not set -# CONFIG_IPV6 is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set - -# -# IP: Netfilter Configuration -# -# CONFIG_IP_NF_CONNTRACK is not set -# CONFIG_IP_NF_CONNTRACK_MARK is not set -# CONFIG_IP_NF_QUEUE is not set -# CONFIG_IP_NF_IPTABLES is not set -# CONFIG_IP_NF_ARPTABLES is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_SCTP is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_NET_DIVERT is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_AF_RXRPC is not set -# CONFIG_AF_RXRPC_DEBUG is not set -# CONFIG_BT is not set -# CONFIG_I2C is not set - CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set # CONFIG_DUMMY is not set # CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set - -# -# Ethernet (10 or 100Mbit) -# +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set CONFIG_NET_ETHERNET=y -# CONFIG_MII is not set +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y # -# Ethernet (1000 Mbit) -# - -# -# Ethernet (10000 Mbit) -# - -# -# Token Ring devices -# - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces +# Wireless LAN # +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set # CONFIG_WAN is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set # CONFIG_SHAPER is not set # CONFIG_NETCONSOLE is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Telephony Support -# -# CONFIG_PHONE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set # # Input device support @@ -470,7 +416,7 @@ CONFIG_NET_ETHERNET=y # CONFIG_INPUT is not set # -# Input I/O drivers +# Hardware I/O ports # CONFIG_SERIO=y # CONFIG_SERIO_I8042 is not set @@ -480,94 +426,39 @@ CONFIG_SERIO=y # CONFIG_GAMEPORT is not set # -# Input Device Drivers -# -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=y -# CONFIG_KEYBOARD_SUNKBD is not set -# CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set -# CONFIG_KEYBOARD_NEWTON is not set -CONFIG_INPUT_MOUSE=y -CONFIG_MOUSE_PS2=y -# CONFIG_MOUSE_SERIAL is not set -# CONFIG_MOUSE_VSXXXAA is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TABLET is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# # Character devices # # CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set +CONFIG_HW_RANDOM=y # CONFIG_RTC is not set # CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set # CONFIG_R3964 is not set -# CONFIG_RTC_LIB is not set -# CONFIG_RTC_CLASS is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set # -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# # File systems # # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set +# CONFIG_EXT4DEV_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set - -# -# XFS support -# +# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set # # CD-ROM/DVD Filesystems @@ -587,13 +478,12 @@ CONFIG_DNOTIFY=y # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y CONFIG_SYSFS=y -# CONFIG_DEVFS_FS is not set -# CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y -# CONFIG_TMPFS_XATTR is not set +# CONFIG_TMPFS_POSIX_ACL is not set # CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems @@ -605,15 +495,15 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -CONFIG_JFFS_FS=y -CONFIG_JFFS_FS_VERBOSE=0 -# CONFIG_JFFS_PROC_FS is not set CONFIG_JFFS2_FS=y CONFIG_JFFS2_FS_DEBUG=0 -# CONFIG_JFFS2_FS_NAND is not set -# CONFIG_JFFS2_FS_NOR_ECC is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set # CONFIG_JFFS2_COMPRESSION_OPTIONS is not set CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set CONFIG_JFFS2_RTIME=y # CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=y @@ -622,12 +512,10 @@ CONFIG_CRAMFS=y # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set - -# -# Network File Systems -# +CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set # CONFIG_NFS_V4 is not set # CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set @@ -649,181 +537,44 @@ CONFIG_SUNRPC=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# # CONFIG_NLS is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# Generic devices -# -# CONFIG_SND_MPU401_UART is not set -# CONFIG_SND_DUMMY is not set -# CONFIG_SND_VIRMIDI is not set -# CONFIG_SND_MTPAV is not set -# CONFIG_SND_SERIAL_U16550 is not set -# CONFIG_SND_MPU401 is not set - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set -# CONFIG_PARPORT_PC_PCMCIA is not set -# CONFIG_NET_PCMCIA is not set - -# -# PC-card bridges -# - -# -# USB support -# -CONFIG_USB=y -# CONFIG_USB_DEBUG is not set - -# -# Miscellaneous USB options -# -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_BANDWIDTH is not set -# CONFIG_USB_DYNAMIC_MINORS is not set -# CONFIG_USB_OTG is not set -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Host Controller Drivers -# -# CONFIG_USB_SL811_HCD is not set - -# -# USB Device Class drivers -# - -# -# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem -# -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set - -# -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information -# -# CONFIG_USB_STORAGE is not set - -# -# USB Input Devices -# -# CONFIG_USB_HID is not set -# HID_SUPPORT is not set - -# -# USB HID Boot Protocol drivers -# -# CONFIG_USB_KBD is not set -# CONFIG_USB_MOUSE is not set -# CONFIG_USB_AIPTEK is not set -# CONFIG_USB_WACOM is not set -# CONFIG_USB_KBTAB is not set -# CONFIG_USB_POWERMATE is not set -# CONFIG_USB_MTOUCH is not set -# CONFIG_USB_EGALAX is not set -# CONFIG_USB_XPAD is not set -# CONFIG_USB_ATI_REMOTE is not set - -# -# USB Imaging devices -# -# CONFIG_USB_MDC800 is not set - -# -# USB Multimedia devices -# -# CONFIG_USB_DABUSB is not set - -# -# Video4Linux support is needed for USB Multimedia device support -# - -# -# USB Network Adapters -# -# CONFIG_USB_CATC is not set -# CONFIG_USB_KAWETH is not set -# CONFIG_USB_PEGASUS is not set -CONFIG_USB_RTL8150=y -# CONFIG_USB_USBNET is not set - -# -# USB port drivers -# - -# -# USB Serial Converter support -# -# CONFIG_USB_SERIAL is not set - -# -# USB Miscellaneous drivers -# -# CONFIG_USB_EMI62 is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_AUERSWALD is not set -# CONFIG_USB_RIO500 is not set -# CONFIG_USB_LEGOTOWER is not set -# CONFIG_USB_LCD is not set -# CONFIG_USB_LED is not set -# CONFIG_USB_CYTHERM is not set -# CONFIG_USB_PHIDGETKIT is not set -# CONFIG_USB_PHIDGETSERVO is not set -# CONFIG_USB_IDMOUSE is not set -# CONFIG_USB_TEST is not set - -# -# USB ATM/DSL drivers -# - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set +# CONFIG_DLM is not set # # Kernel hacking # # CONFIG_PROFILING is not set # CONFIG_SYSTEM_PROFILER is not set -# CONFIG_ETRAX_KGDB is not set -# CONFIG_DEBUG_INFO is not set -# CONFIG_FRAME_POINTER is not set -# CONFIG_DEBUG_NMI_OOPS is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set # # Security options # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set - -# -# Cryptographic options -# +# CONFIG_SECURITY_FILE_CAPABILITIES is not set # CONFIG_CRYPTO is not set # -# Hardware crypto devices -# CONFIG_CRYPTO_HW is not set - -# # Library routines # +CONFIG_BITREVERSE=y # CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set CONFIG_CRC32=y +# CONFIG_CRC7 is not set # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/etraxfs_defconfig b/arch/cris/etraxfs_defconfig new file mode 100644 index 00000000000..73c646a3725 --- /dev/null +++ b/arch/cris/etraxfs_defconfig @@ -0,0 +1,585 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc3 +# Fri Nov 30 14:24:26 2007 +# +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 +CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# General setup +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_ETRAX_FAST_TIMER=y +# CONFIG_ETRAX_KMALLOCED_MODULES is not set +# CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y + +# +# Hardware setup +# +# CONFIG_ETRAX100LX is not set +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +CONFIG_ETRAXFS=y +# CONFIG_CRIS_MACH_ARTPEC3 is not set +# CONFIG_ETRAX_VCS_SIM is not set +# CONFIG_ETRAX_ARCH_V10 is not set +CONFIG_ETRAX_ARCH_V32=y +CONFIG_ETRAX_DRAM_SIZE=32 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 +CONFIG_ETRAX_FLASH1_SIZE=4 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +# CONFIG_ETRAX_DEBUG_PORT_NULL is not set +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 + +# +# ETRAX FS options +# +CONFIG_ETRAX_SERIAL_PORTS=4 +CONFIG_ETRAX_MEM_GRP1_CONFIG=4044a +CONFIG_ETRAX_MEM_GRP2_CONFIG=0 +CONFIG_ETRAX_MEM_GRP3_CONFIG=0 +CONFIG_ETRAX_MEM_GRP4_CONFIG=0 +CONFIG_ETRAX_SDRAM_GRP0_CONFIG=336 +CONFIG_ETRAX_SDRAM_GRP1_CONFIG=0 +CONFIG_ETRAX_SDRAM_TIMING=104a +CONFIG_ETRAX_SDRAM_COMMAND=0 +CONFIG_ETRAX_DEF_GIO_PA_OE=1c +CONFIG_ETRAX_DEF_GIO_PA_OUT=00 +CONFIG_ETRAX_DEF_GIO_PB_OE=00000 +CONFIG_ETRAX_DEF_GIO_PB_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PC_OE=00000 +CONFIG_ETRAX_DEF_GIO_PC_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PD_OE=00000 +CONFIG_ETRAX_DEF_GIO_PD_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PE_OE=00000 +CONFIG_ETRAX_DEF_GIO_PE_OUT=00000 +# CONFIG_CPU_FREQ is not set +# CONFIG_ETRAX_NBR_LED_GRP_ZERO is not set +CONFIG_ETRAX_NBR_LED_GRP_ONE=y +# CONFIG_ETRAX_NBR_LED_GRP_TWO is not set +CONFIG_ETRAX_LED_G_NET0="PA3" +CONFIG_ETRAX_LED_R_NET0="PA4" +CONFIG_ETRAX_V32_LED2G="PA5" +CONFIG_ETRAX_V32_LED2R="PA6" +CONFIG_ETRAX_V32_LED3G="PA7" +CONFIG_ETRAX_V32_LED3R="PA7" + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Drivers for built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +# CONFIG_ETRAX_I2C is not set +# CONFIG_ETRAX_GPIO is not set +# CONFIG_ETRAX_NO_PHY is not set +# CONFIG_ETRAX_ETHERNET_IFACE0 is not set +# CONFIG_ETRAX_ETHERNET_IFACE1 is not set +# CONFIG_ETRAXFS_SERIAL is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE is not set +# CONFIG_ETRAX_NANDFLASH is not set +# CONFIG_ETRAX_CARDBUS is not set +# CONFIG_ETRAX_IOP_FW_LOAD is not set +# CONFIG_ETRAX_STREAMCOPROC is not set +# CONFIG_ETRAX_SPI_MMC is not set +# CONFIG_ETRAX_SPI_MMC_BOARD is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_RAM=y +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=y +CONFIG_MTDRAM_TOTAL_SIZE=0 +CONFIG_MTDRAM_ERASE_SIZE=64 +CONFIG_MTDRAM_ABS_POS=0x0 +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_HW_RANDOM=y +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PROFILING is not set +# CONFIG_SYSTEM_PROFILER is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/kernel/module.c b/arch/cris/kernel/module.c index 11b867df861..a187833febc 100644 --- a/arch/cris/kernel/module.c +++ b/arch/cris/kernel/module.c @@ -28,20 +28,28 @@ #define DEBUGP(fmt , ...) #endif +#ifdef CONFIG_ETRAX_KMALLOCED_MODULES +#define MALLOC_MODULE(size) kmalloc(size, GFP_KERNEL) +#define FREE_MODULE(region) kfree(region) +#else +#define MALLOC_MODULE(size) vmalloc_exec(size) +#define FREE_MODULE(region) vfree(region) +#endif + void *module_alloc(unsigned long size) { if (size == 0) return NULL; - return vmalloc_exec(size); + return MALLOC_MODULE(size); } /* Free memory returned from module_alloc */ void module_free(struct module *mod, void *module_region) { - vfree(module_region); + FREE_MODULE(module_region); /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ + table entries. */ } /* We don't need anything special. */ diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c index 9ca558fc5bc..ef2db8fd102 100644 --- a/arch/cris/kernel/process.c +++ b/arch/cris/kernel/process.c @@ -1,5 +1,4 @@ -/* $Id: process.c,v 1.21 2005/03/04 08:16:17 starvik Exp $ - * +/* * linux/arch/cris/kernel/process.c * * Copyright (C) 1995 Linus Torvalds @@ -7,105 +6,6 @@ * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: process.c,v $ - * Revision 1.21 2005/03/04 08:16:17 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.20 2005/01/18 05:57:22 starvik - * Renamed hlt_counter to cris_hlt_counter and made it global. - * - * Revision 1.19 2004/10/19 13:07:43 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.18 2004/08/16 12:37:23 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.17 2004/04/05 13:53:48 starvik - * Merge of Linux 2.6.5 - * - * Revision 1.16 2003/10/27 08:04:33 starvik - * Merge of Linux 2.6.0-test9 - * - * Revision 1.15 2003/09/11 07:29:52 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.14 2003/06/10 10:21:12 johana - * Moved thread_saved_pc() from arch/cris/kernel/process.c to - * subarch specific process.c. arch-v32 has an erp, no irp. - * - * Revision 1.13 2003/04/09 05:20:47 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.12 2002/12/11 15:41:11 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/kernel - * - * Revision 1.11 2002/12/10 09:00:10 starvik - * Merge of Linux 2.5.51 - * - * Revision 1.10 2002/11/27 08:42:34 starvik - * Argument to user_regs() is thread_info* - * - * Revision 1.9 2002/11/26 09:44:21 starvik - * New threads exits through ret_from_fork (necessary for preemptive scheduling) - * - * Revision 1.8 2002/11/19 14:35:24 starvik - * Changes from linux 2.4 - * Changed struct initializer syntax to the currently prefered notation - * - * Revision 1.7 2002/11/18 07:39:42 starvik - * thread_saved_pc moved here from processor.h - * - * Revision 1.6 2002/11/14 06:51:27 starvik - * Made cpu_idle more similar with other archs - * init_task_union -> init_thread_union - * Updated for new interrupt macros - * sys_clone and do_fork have a new argument, user_tid - * - * Revision 1.5 2002/11/05 06:45:11 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.4 2002/02/05 15:37:44 bjornw - * Need init_task.h - * - * Revision 1.3 2002/01/21 15:22:49 bjornw - * current->counter is gone - * - * Revision 1.22 2001/11/13 09:40:43 orjanf - * Added dump_fpu (needed for core dumps). - * - * Revision 1.21 2001/11/12 18:26:21 pkj - * Fixed compiler warnings. - * - * Revision 1.20 2001/10/03 08:21:39 jonashg - * cause_of_death does not exist if CONFIG_SVINTO_SIM is defined. - * - * Revision 1.19 2001/09/26 11:52:54 bjornw - * INIT_MMAP is gone in 2.4.10 - * - * Revision 1.18 2001/08/21 21:43:51 hp - * Move last watchdog fix inside #ifdef CONFIG_ETRAX_WATCHDOG - * - * Revision 1.17 2001/08/21 13:48:01 jonashg - * Added fix by HP to avoid oops when doing a hard_reset_now. - * - * Revision 1.16 2001/06/21 02:00:40 hp - * * entry.S: Include asm/unistd.h. - * (_sys_call_table): Use section .rodata, not .data. - * (_kernel_thread): Move from... - * * process.c: ... here. - * * entryoffsets.c (VAL): Break out from... - * (OF): Use VAL. - * (LCLONE_VM): New asmified value from CLONE_VM. - * - * Revision 1.15 2001/06/20 16:31:57 hp - * Add comments to describe empty functions according to review. - * - * Revision 1.14 2001/05/29 11:27:59 markusl - * Fixed so that hard_reset_now will do reset even if watchdog wasn't enabled - * - * Revision 1.13 2001/03/20 19:44:06 bjornw - * Use the 7th syscall argument for regs instead of current_regs - * */ /* @@ -206,6 +106,7 @@ EXPORT_SYMBOL(pm_power_off); * low exit latency (ie sit in a loop waiting for * somebody to say that they'd like to reschedule) */ + void cpu_idle (void) { /* endless idle loop with no priority at all */ diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c index 3ccd20e85dc..b326023baab 100644 --- a/arch/cris/kernel/ptrace.c +++ b/arch/cris/kernel/ptrace.c @@ -2,65 +2,11 @@ * linux/arch/cris/kernel/ptrace.c * * Parts taken from the m68k port. - * + * * Copyright (c) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen * - * $Log: ptrace.c,v $ - * Revision 1.10 2004/09/22 11:50:01 orjanf - * * Moved get_reg/put_reg to arch-specific files. - * * Added functions to access debug registers (CRISv32). - * * Added support for PTRACE_SINGLESTEP (CRISv32). - * * Added S flag to CCS_MASK (CRISv32). - * - * Revision 1.9 2003/07/04 12:56:11 tobiasa - * Moved arch-specific code to arch-specific files. - * - * Revision 1.8 2003/04/09 05:20:47 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.7 2002/11/27 08:42:34 starvik - * Argument to user_regs() is thread_info* - * - * Revision 1.6 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.5 2002/11/18 07:41:19 starvik - * Removed warning - * - * Revision 1.4 2002/11/11 12:47:28 starvik - * SYSCALL_TRACE has been moved to thread flags - * - * Revision 1.3 2002/02/05 15:37:18 bjornw - * * Add do_notify_resume (replaces do_signal in the callchain) - * * syscall_trace is now do_syscall_trace - * * current->ptrace flag PT_TRACESYS -> PT_SYSCALLTRACE - * * Keep track of the current->work.syscall_trace counter - * - * Revision 1.2 2001/12/18 13:35:20 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.8 2001/11/12 18:26:21 pkj - * Fixed compiler warnings. - * - * Revision 1.7 2001/09/26 11:53:49 bjornw - * PTRACE_DETACH works more simple in 2.4.10 - * - * Revision 1.6 2001/07/25 16:08:47 bjornw - * PTRACE_ATTACH bulk moved into arch-independent code in 2.4.7 - * - * Revision 1.5 2001/03/26 14:24:28 orjanf - * * Changed loop condition. - * * Added comment documenting non-standard ptrace behaviour. - * - * Revision 1.4 2001/03/20 19:44:41 bjornw - * Use the user_regs macro instead of thread.esp0 - * - * Revision 1.3 2000/12/18 23:45:25 bjornw - * Linux/CRIS first version - * - * */ #include <linux/kernel.h> @@ -85,7 +31,7 @@ extern int do_signal(int canrestart, struct pt_regs *regs); void do_notify_resume(int canrestart, struct pt_regs *regs, - __u32 thread_info_flags ) + __u32 thread_info_flags) { /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) diff --git a/arch/cris/kernel/semaphore.c b/arch/cris/kernel/semaphore.c index b884263d3cd..f137a439041 100644 --- a/arch/cris/kernel/semaphore.c +++ b/arch/cris/kernel/semaphore.c @@ -4,7 +4,6 @@ */ #include <linux/sched.h> -#include <linux/init.h> #include <asm/semaphore-helper.h> /* diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 4da042e100a..04d48dd91dd 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c @@ -18,7 +18,7 @@ #include <linux/screen_info.h> #include <linux/utsname.h> #include <linux/pfn.h> - +#include <linux/cpu.h> #include <asm/setup.h> /* @@ -36,6 +36,8 @@ extern unsigned long dram_start, dram_end; extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ +static struct cpu cpu_devices[NR_CPUS]; + extern void show_etrax_copyright(void); /* arch-vX/kernel/setup.c */ /* This mainly sets up the memory area, and can be really confusing. @@ -45,24 +47,23 @@ extern void show_etrax_copyright(void); /* arch-vX/kernel/setup.c */ * given by the macro __pa(). * * In this DRAM, the kernel code and data is loaded, in the beginning. - * It really starts at c0004000 to make room for some special pages - + * It really starts at c0004000 to make room for some special pages - * the start address is text_start. The kernel data ends at _end. After * this the ROM filesystem is appended (if there is any). - * + * * Between this address and dram_end, we have RAM pages usable to the * boot code and the system. * */ -void __init -setup_arch(char **cmdline_p) +void __init setup_arch(char **cmdline_p) { extern void init_etrax_debug(void); unsigned long bootmap_size; unsigned long start_pfn, max_pfn; unsigned long memory_start; - /* register an initial console printing routine for printk's */ + /* register an initial console printing routine for printk's */ init_etrax_debug(); @@ -121,7 +122,7 @@ setup_arch(char **cmdline_p) min_low_pfn = PAGE_OFFSET >> PAGE_SHIFT; bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, - min_low_pfn, + min_low_pfn, max_low_pfn); /* And free all memory not belonging to the kernel (addr, size) */ @@ -180,11 +181,23 @@ static void c_stop(struct seq_file *m, void *v) extern int show_cpuinfo(struct seq_file *m, void *v); -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, .show = show_cpuinfo, }; +static int __init topology_init(void) +{ + int i; + + for_each_possible_cpu(i) { + return register_cpu(&cpu_devices[i], i); + } + + return 0; +} + +subsys_initcall(topology_init); diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index 7a2cc7efbcf..ff4c6aa75de 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c @@ -1,5 +1,4 @@ -/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $ - * +/* * linux/arch/cris/kernel/time.c * * Copyright (C) 1991, 1992, 1995 Linus Torvalds @@ -18,7 +17,7 @@ * Linux/CRIS specific code: * * Authors: Bjorn Wesen - * Johan Adolfsson + * Johan Adolfsson * */ @@ -208,10 +207,16 @@ cris_do_profile(struct pt_regs* regs) #endif #ifdef CONFIG_PROFILING - profile_tick(CPU_PROFILING); + profile_tick(CPU_PROFILING); #endif } +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ) + + get_ns_in_jiffie(); +} + static int __init init_udelay(void) { diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c index 520d92205fe..541efbf0937 100644 --- a/arch/cris/kernel/traps.c +++ b/arch/cris/kernel/traps.c @@ -1,66 +1,78 @@ -/* $Id: traps.c,v 1.11 2005/01/24 16:03:19 orjanf Exp $ - * +/* * linux/arch/cris/traps.c * - * Here we handle the break vectors not used by the system call - * mechanism, as well as some general stack/register dumping + * Here we handle the break vectors not used by the system call + * mechanism, as well as some general stack/register dumping * things. - * - * Copyright (C) 2000-2002 Axis Communications AB + * + * Copyright (C) 2000-2007 Axis Communications AB * * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Hans-Peter Nilsson * */ #include <linux/init.h> #include <linux/module.h> + #include <asm/pgtable.h> #include <asm/uaccess.h> +extern void arch_enable_nmi(void); +extern void stop_watchdog(void); +extern void reset_watchdog(void); +extern void show_registers(struct pt_regs *regs); + +#ifdef CONFIG_DEBUG_BUGVERBOSE +extern void handle_BUG(struct pt_regs *regs); +#else +#define handle_BUG(regs) +#endif + static int kstack_depth_to_print = 24; -extern int raw_printk(const char *fmt, ...); +void (*nmi_handler)(struct pt_regs *); -void show_trace(unsigned long * stack) +void +show_trace(unsigned long *stack) { unsigned long addr, module_start, module_end; extern char _stext, _etext; int i; - raw_printk("\nCall Trace: "); + printk("\nCall Trace: "); - i = 1; - module_start = VMALLOC_START; - module_end = VMALLOC_END; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; - while (((long) stack & (THREAD_SIZE-1)) != 0) { - if (__get_user (addr, stack)) { + while (((long)stack & (THREAD_SIZE-1)) != 0) { + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (((addr >= (unsigned long) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - raw_printk("\n "); - raw_printk("[<%08lx>] ", addr); - i++; - } - } + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long)&_stext) && + (addr <= (unsigned long)&_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } } /* @@ -78,109 +90,149 @@ void show_trace(unsigned long * stack) * with the ksymoops maintainer. */ -void +void show_stack(struct task_struct *task, unsigned long *sp) { - unsigned long *stack, addr; - int i; + unsigned long *stack, addr; + int i; /* * debugging aid: "show_stack(NULL);" prints a * back trace. */ - if(sp == NULL) { + if (sp == NULL) { if (task) sp = (unsigned long*)task->thread.ksp; else sp = (unsigned long*)rdsp(); } - stack = sp; + stack = sp; - raw_printk("\nStack from %08lx:\n ", (unsigned long)stack); - for(i = 0; i < kstack_depth_to_print; i++) { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - if (i && ((i % 8) == 0)) - raw_printk("\n "); - if (__get_user (addr, stack)) { + printk("\nStack from %08lx:\n ", (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long)stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - raw_printk("%08lx ", addr); - } + printk("%08lx ", addr); + } show_trace(sp); } -static void (*nmi_handler)(struct pt_regs*); -extern void arch_enable_nmi(void); +#if 0 +/* displays a short stack trace */ -void set_nmi_handler(void (*handler)(struct pt_regs*)) +int +show_stack(void) { - nmi_handler = handler; - arch_enable_nmi(); + unsigned long *sp = (unsigned long *)rdusp(); + int i; + + printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + for (i = 0; i < 16; i++) + printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + return 0; } +#endif -void handle_nmi(struct pt_regs* regs) +void +dump_stack(void) { - if (nmi_handler) - nmi_handler(regs); + show_stack(NULL, NULL); +} +EXPORT_SYMBOL(dump_stack); + +void +set_nmi_handler(void (*handler)(struct pt_regs *)) +{ + nmi_handler = handler; + arch_enable_nmi(); } #ifdef CONFIG_DEBUG_NMI_OOPS -void oops_nmi_handler(struct pt_regs* regs) +void +oops_nmi_handler(struct pt_regs *regs) { - stop_watchdog(); - raw_printk("NMI!\n"); - show_registers(regs); + stop_watchdog(); + oops_in_progress = 1; + printk("NMI!\n"); + show_registers(regs); + oops_in_progress = 0; } -static int -__init oops_nmi_register(void) +static int __init +oops_nmi_register(void) { - set_nmi_handler(oops_nmi_handler); - return 0; + set_nmi_handler(oops_nmi_handler); + return 0; } __initcall(oops_nmi_register); #endif -#if 0 -/* displays a short stack trace */ - -int -show_stack() +/* + * This gets called from entry.S when the watchdog has bitten. Show something + * similiar to an Oops dump, and if the kernel is configured to be a nice + * doggy, then halt instead of reboot. + */ +void +watchdog_bite_hook(struct pt_regs *regs) { - unsigned long *sp = (unsigned long *)rdusp(); - int i; - raw_printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); - for(i = 0; i < 16; i++) - raw_printk("sp + %d: 0x%08lx\n", i*4, sp[i]); - return 0; -} +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + + while (1) + ; /* Do nothing. */ +#else + show_registers(regs); #endif +} -void dump_stack(void) +/* This is normally the Oops function. */ +void +die_if_kernel(const char *str, struct pt_regs *regs, long err) { - show_stack(NULL, NULL); -} + if (user_mode(regs)) + return; -EXPORT_SYMBOL(dump_stack); +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* + * This printout might take too long and could trigger + * the watchdog normally. If NICE_DOGGY is set, simply + * stop the watchdog during the printout. + */ + stop_watchdog(); +#endif -void __init -trap_init(void) -{ - /* Nothing needs to be done */ + handle_BUG(regs); + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + + oops_in_progress = 0; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + do_exit(SIGSEGV); } -void spinning_cpu(void* addr) +void __init +trap_init(void) { - raw_printk("CPU %d spinning on %X\n", smp_processor_id(), addr); - dump_stack(); + /* Nothing needs to be done */ } diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index 3034f3ff950..c4c76db90f9 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -1,130 +1,9 @@ /* * linux/arch/cris/mm/fault.c * - * Copyright (C) 2000, 2001 Axis Communications AB - * - * Authors: Bjorn Wesen - * - * $Log: fault.c,v $ - * Revision 1.20 2005/03/04 08:16:18 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.19 2005/01/14 10:07:59 starvik - * Fixed warning. - * - * Revision 1.18 2005/01/12 08:10:14 starvik - * Re-added the change of frametype when handling kernel page fault fixup - * for v10. This is necessary to avoid that the CPU remakes the faulting - * access. - * - * Revision 1.17 2005/01/11 13:53:05 starvik - * Use raw_printk. - * - * Revision 1.16 2004/12/17 11:39:41 starvik - * SMP support. - * - * Revision 1.15 2004/11/23 18:36:18 starvik - * Stack is now non-executable. - * Signal handler trampolines are placed in a reserved page mapped into all - * processes. - * - * Revision 1.14 2004/11/23 07:10:21 starvik - * Moved find_fixup_code to generic code. - * - * Revision 1.13 2004/11/23 07:00:54 starvik - * Actually use the execute permission bit in the MMU. This makes it possible - * to prevent e.g. attacks where executable code is put on the stack. - * - * Revision 1.12 2004/09/29 06:16:04 starvik - * Use instruction_pointer - * - * Revision 1.11 2004/05/14 07:58:05 starvik - * Merge of changes from 2.4 - * - * Revision 1.10 2003/10/27 14:51:24 starvik - * Removed debugcode - * - * Revision 1.9 2003/10/27 14:50:42 starvik - * Changed do_page_fault signature - * - * Revision 1.8 2003/07/04 13:02:48 tobiasa - * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code - * to separate function in arch-specific files. - * - * Revision 1.7 2003/01/22 06:48:38 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.6 2003/01/09 14:42:52 starvik - * Merge of Linux 2.5.55 - * - * Revision 1.5 2002/12/11 14:44:48 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm - * - * Revision 1.4 2002/11/13 15:10:28 starvik - * pte_offset has been renamed to pte_offset_kernel - * - * Revision 1.3 2002/11/05 06:45:13 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.2 2001/12/18 13:35:22 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.20 2001/11/22 13:34:06 bjornw - * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted - * unaligned write, because the second half of the write will be corrupted - * otherwise. Affected unaligned writes spanning not-yet mapped pages. - * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss - * was due to a read or a write (before we didn't know this until the next - * restart of the interrupted instruction, thus wasting one fault-irq) - * - * Revision 1.19 2001/11/12 19:02:10 pkj - * Fixed compiler warnings. - * - * Revision 1.18 2001/07/18 22:14:32 bjornw - * Enable interrupts in the bulk of do_page_fault - * - * Revision 1.17 2001/07/18 13:07:23 bjornw - * * Detect non-existant PTE's in vmalloc pmd synchronization - * * Remove comment about fast-paths for VMALLOC_START etc, because all that - * was totally bogus anyway it turned out :) - * * Fix detection of vmalloc-area synchronization - * * Add some comments - * - * Revision 1.16 2001/06/13 00:06:08 bjornw - * current_pgd should be volatile - * - * Revision 1.15 2001/06/13 00:02:23 bjornw - * Use a separate variable to store the current pgd to avoid races in schedule - * - * Revision 1.14 2001/05/16 17:41:07 hp - * Last comment tweak further tweaked. - * - * Revision 1.13 2001/05/15 00:58:44 hp - * Expand a bit on the comment why we compare address >= TASK_SIZE rather - * than >= VMALLOC_START. - * - * Revision 1.12 2001/04/04 10:51:14 bjornw - * mmap_sem is grabbed for reading - * - * Revision 1.11 2001/03/23 07:36:07 starvik - * Corrected according to review remarks - * - * Revision 1.10 2001/03/21 16:10:11 bjornw - * CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL - * - * Revision 1.9 2001/03/05 13:22:20 bjornw - * Spell-fix and fix in vmalloc_fault handling - * - * Revision 1.8 2000/11/22 14:45:31 bjornw - * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping - * into all processes. Instead we fill in the missing PTE entries on demand. - * - * Revision 1.7 2000/11/21 16:39:09 bjornw - * fixup switches frametype - * - * Revision 1.6 2000/11/17 16:54:08 bjornw - * More detailed siginfo reporting + * Copyright (C) 2000-2006 Axis Communications AB * + * Authors: Bjorn Wesen * */ @@ -135,7 +14,6 @@ extern int find_fixup_code(struct pt_regs *); extern void die_if_kernel(const char *, struct pt_regs *, long); -extern int raw_printk(const char *fmt, ...); /* debug of low-level TLB reload */ #undef DEBUG @@ -164,8 +42,8 @@ unsigned long cris_signal_return_page; * address. * * error_code: - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write * * If this routine detects a bad access, it returns 1, otherwise it * returns 0. @@ -181,9 +59,10 @@ do_page_fault(unsigned long address, struct pt_regs *regs, siginfo_t info; int fault; - D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", - address, smp_processor_id(), instruction_pointer(regs), - protection, writeaccess)); + D(printk(KERN_DEBUG + "Page fault for %lX on %X at %lX, prot %d write %d\n", + address, smp_processor_id(), instruction_pointer(regs), + protection, writeaccess)); tsk = current; @@ -233,7 +112,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * context, we must not take the fault.. */ - if (in_atomic() || !mm) + if (in_interrupt() || !mm) goto no_context; down_read(&mm->mmap_sem); @@ -319,6 +198,9 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* info.si_code has been set above */ info.si_addr = (void *)address; force_sig_info(SIGSEGV, &info, tsk); + printk(KERN_NOTICE "%s (pid %d) segfaults for page " + "address %08lx at pc %08lx\n", + tsk->comm, tsk->pid, address, instruction_pointer(regs)); return; } @@ -326,7 +208,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* Are we prepared to handle this kernel fault? * - * (The kernel has valid exception-points in the source + * (The kernel has valid exception-points in the source * when it acesses user-memory. When it fails in one * of those points, we find it in a table and do a jump * to some fixup code that loads an appropriate error @@ -341,13 +223,18 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * terminate things with extreme prejudice. */ - if ((unsigned long) (address) < PAGE_SIZE) - raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - raw_printk(KERN_ALERT "Unable to handle kernel access"); - raw_printk(" at virtual address %08lx\n",address); - - die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + if (!oops_in_progress) { + oops_in_progress = 1; + if ((unsigned long) (address) < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL " + "pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access" + " at virtual address %08lx\n", address); + + die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + oops_in_progress = 0; + } do_exit(SIGKILL); @@ -360,7 +247,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, up_read(&mm->mmap_sem); printk("VM: killing process %s\n", tsk->comm); if (user_mode(regs)) - do_group_exit(SIGKILL); + do_exit(SIGKILL); goto no_context; do_sigbus: @@ -406,8 +293,8 @@ vmalloc_fault: /* Since we're two-level, we don't need to do both * set_pgd and set_pmd (they do the same thing). If * we go three-level at some point, do the right thing - * with pgd_present and set_pgd here. - * + * with pgd_present and set_pgd here. + * * Also, since the vmalloc area is global, we don't * need to copy individual PTE's, it is enough to * copy the pgd pointer into the pte page of the diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index 0c833d17622..4207a2b5275 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -6,117 +6,6 @@ * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: init.c,v $ - * Revision 1.11 2004/05/28 09:28:56 starvik - * Calculation of loops_per_usec moved because initialization order has changed - * in Linux 2.6. - * - * Revision 1.10 2004/05/14 07:58:05 starvik - * Merge of changes from 2.4 - * - * Revision 1.9 2003/07/04 08:27:54 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.8 2003/04/09 05:20:48 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.7 2003/01/22 06:48:38 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.6 2002/12/11 14:44:48 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm - * - * Revision 1.5 2002/11/18 07:37:37 starvik - * Added cache bug workaround (from Linux 2.4) - * - * Revision 1.4 2002/11/13 15:40:24 starvik - * Removed the page table cache stuff (as done in other archs) - * - * Revision 1.3 2002/11/05 06:45:13 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.2 2001/12/18 13:35:22 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.31 2001/11/13 16:22:00 bjornw - * Skip calculating totalram and sharedram in si_meminfo - * - * Revision 1.30 2001/11/12 19:02:10 pkj - * Fixed compiler warnings. - * - * Revision 1.29 2001/07/25 16:09:50 bjornw - * val->sharedram will stay 0 - * - * Revision 1.28 2001/06/28 16:30:17 bjornw - * Oops. This needs to wait until 2.4.6 is merged - * - * Revision 1.27 2001/06/28 14:04:07 bjornw - * Fill in sharedram - * - * Revision 1.26 2001/06/18 06:36:02 hp - * Enable free_initmem of __init-type pages - * - * Revision 1.25 2001/06/13 00:02:23 bjornw - * Use a separate variable to store the current pgd to avoid races in schedule - * - * Revision 1.24 2001/05/15 00:52:20 hp - * Only map segment 0xa as seg if CONFIG_JULIETTE - * - * Revision 1.23 2001/04/04 14:35:40 bjornw - * * Removed get_pte_slow and friends (2.4.3 change) - * * Removed bad_pmd handling (2.4.3 change) - * - * Revision 1.22 2001/04/04 13:38:04 matsfg - * Moved ioremap to a separate function instead - * - * Revision 1.21 2001/03/27 09:28:33 bjornw - * ioremap used too early - lets try it in mem_init instead - * - * Revision 1.20 2001/03/23 07:39:21 starvik - * Corrected according to review remarks - * - * Revision 1.19 2001/03/15 14:25:17 bjornw - * More general shadow registers and ioremaped addresses for external I/O - * - * Revision 1.18 2001/02/23 12:46:44 bjornw - * * 0xc was not CSE1; 0x8 is, same as uncached flash, so we move the uncached - * flash during CRIS_LOW_MAP from 0xe to 0x8 so both the flash and the I/O - * is mapped straight over (for !CRIS_LOW_MAP the uncached flash is still 0xe) - * - * Revision 1.17 2001/02/22 15:05:21 bjornw - * Map 0x9 straight over during LOW_MAP to allow for memory mapped LEDs - * - * Revision 1.16 2001/02/22 15:02:35 bjornw - * Map 0xc straight over during LOW_MAP to allow for memory mapped I/O - * - * Revision 1.15 2001/01/10 21:12:10 bjornw - * loops_per_sec -> loops_per_jiffy - * - * Revision 1.14 2000/11/22 16:23:20 bjornw - * Initialize totalhigh counters to 0 to make /proc/meminfo look nice. - * - * Revision 1.13 2000/11/21 16:37:51 bjornw - * Temporarily disable initmem freeing - * - * Revision 1.12 2000/11/21 13:55:07 bjornw - * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type - * - * Revision 1.11 2000/10/06 12:38:22 bjornw - * Cast empty_bad_page correctly (should really be of * type from the start.. - * - * Revision 1.10 2000/10/04 16:53:57 bjornw - * Fix memory-map due to LX features - * - * Revision 1.9 2000/09/13 15:47:49 bjornw - * Wrong count in reserved-pages loop - * - * Revision 1.8 2000/09/13 14:35:10 bjornw - * 2.4.0-test8 added a new arg to free_area_init_node - * - * Revision 1.7 2000/08/17 15:35:55 bjornw - * 2.4.0-test6 removed MAP_NR and inserted virt_to_page - * - * */ #include <linux/init.h> diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index 96f7d70f447..9e561ede092 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -57,6 +57,10 @@ config ARCH_HAS_ILOG2_U64 bool default y +config HZ + int + default 1000 + mainmenu "Fujitsu FR-V Kernel Configuration" source "init/Kconfig" diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index 6c01464db69..0669e138238 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -1113,7 +1113,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c index 1a2e5c8d03a..66f616fb486 100644 --- a/arch/frv/mm/pgalloc.c +++ b/arch/frv/mm/pgalloc.c @@ -28,7 +28,7 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *page; @@ -37,9 +37,11 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #else page = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); #endif - if (page) + if (page) { clear_highpage(page); - flush_dcache_page(page); + pgtable_page_ctor(page); + flush_dcache_page(page); + } return page; } diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index dc61222e112..f69e5ea3855 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -65,6 +65,9 @@ config TIME_LOW_RES bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + config NO_IOPORT def_bool y @@ -79,6 +82,10 @@ config PCI bool default n +config HZ + int + default 100 + source "init/Kconfig" source "arch/h8300/Kconfig.cpu" diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c index cd3734614d9..b1f25c20a5d 100644 --- a/arch/h8300/kernel/setup.c +++ b/arch/h8300/kernel/setup.c @@ -236,7 +236,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index f5d3efbfbed..3c331c464b4 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1573,7 +1573,7 @@ sys_call_table: data8 sys_fchmodat data8 sys_faccessat data8 sys_pselect6 - data8 sys_ppoll + data8 sys_ppoll // 1295 data8 sys_unshare data8 sys_splice data8 sys_set_robust_list @@ -1588,5 +1588,8 @@ sys_call_table: data8 sys_signalfd data8 sys_ni_syscall data8 sys_eventfd + data8 sys_timerfd_create // 1310 + data8 sys_timerfd_settime + data8 sys_timerfd_gettime .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 846e7e036b1..6e17aed5313 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -17,7 +17,7 @@ * Copyright (C) 2000 Intel * Copyright (C) Chuck Fleckenstein <cfleck@co.intel.com> * - * Copyright (C) 1999, 2004 Silicon Graphics, Inc. + * Copyright (C) 1999, 2004-2008 Silicon Graphics, Inc. * Copyright (C) Vijay Chander <vijay@engr.sgi.com> * * Copyright (C) 2006 FUJITSU LIMITED @@ -1762,11 +1762,8 @@ format_mca_init_stack(void *mca_data, unsigned long offset, /* Caller prevents this from being called after init */ static void * __init_refok mca_bootmem(void) { - void *p; - - p = alloc_bootmem(sizeof(struct ia64_mca_cpu) * NR_CPUS + - KERNEL_STACK_SIZE); - return (void *)ALIGN((unsigned long)p, KERNEL_STACK_SIZE); + return __alloc_bootmem(sizeof(struct ia64_mca_cpu), + KERNEL_STACK_SIZE, 0); } /* Do per-CPU MCA-related initialization. */ @@ -1774,33 +1771,33 @@ void __cpuinit ia64_mca_cpu_init(void *cpu_data) { void *pal_vaddr; + void *data; + long sz = sizeof(struct ia64_mca_cpu); + int cpu = smp_processor_id(); static int first_time = 1; - if (first_time) { - void *mca_data; - int cpu; - - first_time = 0; - mca_data = mca_bootmem(); - for (cpu = 0; cpu < NR_CPUS; cpu++) { - format_mca_init_stack(mca_data, - offsetof(struct ia64_mca_cpu, mca_stack), - "MCA", cpu); - format_mca_init_stack(mca_data, - offsetof(struct ia64_mca_cpu, init_stack), - "INIT", cpu); - __per_cpu_mca[cpu] = __pa(mca_data); - mca_data += sizeof(struct ia64_mca_cpu); - } - } - /* - * The MCA info structure was allocated earlier and its - * physical address saved in __per_cpu_mca[cpu]. Copy that - * address * to ia64_mca_data so we can access it as a per-CPU - * variable. + * Structure will already be allocated if cpu has been online, + * then offlined. */ - __get_cpu_var(ia64_mca_data) = __per_cpu_mca[smp_processor_id()]; + if (__per_cpu_mca[cpu]) { + data = __va(__per_cpu_mca[cpu]); + } else { + if (first_time) { + data = mca_bootmem(); + first_time = 0; + } else + data = page_address(alloc_pages_node(numa_node_id(), + GFP_KERNEL, get_order(sz))); + if (!data) + panic("Could not allocate MCA memory for cpu %d\n", + cpu); + } + format_mca_init_stack(data, offsetof(struct ia64_mca_cpu, mca_stack), + "MCA", cpu); + format_mca_init_stack(data, offsetof(struct ia64_mca_cpu, init_stack), + "INIT", cpu); + __get_cpu_var(ia64_mca_data) = __per_cpu_mca[cpu] = __pa(data); /* * Stash away a copy of the PTE needed to map the per-CPU page. diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 78acd9fe97e..f6b99719f10 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -586,21 +586,6 @@ pfm_put_task(struct task_struct *task) } static inline void -pfm_set_task_notify(struct task_struct *task) -{ - struct thread_info *info; - - info = (struct thread_info *) ((char *) task + IA64_TASK_SIZE); - set_bit(TIF_PERFMON_WORK, &info->flags); -} - -static inline void -pfm_clear_task_notify(void) -{ - clear_thread_flag(TIF_PERFMON_WORK); -} - -static inline void pfm_reserve_page(unsigned long a) { SetPageReserved(vmalloc_to_page((void *)a)); @@ -3724,7 +3709,7 @@ pfm_restart(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) PFM_SET_WORK_PENDING(task, 1); - pfm_set_task_notify(task); + tsk_set_notify_resume(task); /* * XXX: send reschedule if task runs on another CPU @@ -5082,7 +5067,7 @@ pfm_handle_work(void) PFM_SET_WORK_PENDING(current, 0); - pfm_clear_task_notify(); + tsk_clear_notify_resume(current); regs = task_pt_regs(current); @@ -5450,7 +5435,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str * when coming from ctxsw, current still points to the * previous task, therefore we must work with task and not current. */ - pfm_set_task_notify(task); + tsk_set_notify_resume(task); } /* * defer until state is changed (shorten spin window). the context is locked diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 7377d323131..49937a383b2 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -52,7 +52,6 @@ #include "sigframe.h" void (*ia64_mark_idle)(int); -static DEFINE_PER_CPU(unsigned int, cpu_idle_state); unsigned long boot_option_idle_override = 0; EXPORT_SYMBOL(boot_option_idle_override); @@ -157,6 +156,17 @@ show_regs (struct pt_regs *regs) show_stack(NULL, NULL); } +void tsk_clear_notify_resume(struct task_struct *tsk) +{ +#ifdef CONFIG_PERFMON + if (tsk->thread.pfm_needs_checking) + return; +#endif + if (test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_RSE)) + return; + clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME); +} + void do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall) { @@ -175,6 +185,10 @@ do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall /* deal with pending signal delivery */ if (test_thread_flag(TIF_SIGPENDING)||test_thread_flag(TIF_RESTORE_SIGMASK)) ia64_do_signal(scr, in_syscall); + + /* copy user rbs to kernel rbs */ + if (unlikely(test_thread_flag(TIF_RESTORE_RSE))) + ia64_sync_krbs(); } static int pal_halt = 1; @@ -239,33 +253,23 @@ static inline void play_dead(void) } #endif /* CONFIG_HOTPLUG_CPU */ -void cpu_idle_wait(void) +static void do_nothing(void *unused) { - unsigned int cpu, this_cpu = get_cpu(); - cpumask_t map; - cpumask_t tmp = current->cpus_allowed; - - set_cpus_allowed(current, cpumask_of_cpu(this_cpu)); - put_cpu(); - - cpus_clear(map); - for_each_online_cpu(cpu) { - per_cpu(cpu_idle_state, cpu) = 1; - cpu_set(cpu, map); - } - - __get_cpu_var(cpu_idle_state) = 0; +} - wmb(); - do { - ssleep(1); - for_each_online_cpu(cpu) { - if (cpu_isset(cpu, map) && !per_cpu(cpu_idle_state, cpu)) - cpu_clear(cpu, map); - } - cpus_and(map, map, cpu_online_map); - } while (!cpus_empty(map)); - set_cpus_allowed(current, tmp); +/* + * cpu_idle_wait - Used to ensure that all the CPUs discard old value of + * pm_idle and update to new pm_idle value. Required while changing pm_idle + * handler on SMP systems. + * + * Caller must have changed pm_idle to the new value before the call. Old + * pm_idle value will not be used by any CPU after the return of this function. + */ +void cpu_idle_wait(void) +{ + smp_mb(); + /* kick all the CPUs so that they exit out of pm_idle */ + smp_call_function(do_nothing, NULL, 0, 1); } EXPORT_SYMBOL_GPL(cpu_idle_wait); @@ -293,9 +297,6 @@ cpu_idle (void) #ifdef CONFIG_SMP min_xtp(); #endif - if (__get_cpu_var(cpu_idle_state)) - __get_cpu_var(cpu_idle_state) = 0; - rmb(); if (mark_idle) (*mark_idle)(1); diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 2e96f17b2f3..331d6768b5d 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -547,6 +547,129 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw, return 0; } +static long +ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw, + unsigned long user_rbs_start, unsigned long user_rbs_end) +{ + unsigned long addr, val; + long ret; + + /* now copy word for word from user rbs to kernel rbs: */ + for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) { + if (access_process_vm(child, addr, &val, sizeof(val), 0) + != sizeof(val)) + return -EIO; + + ret = ia64_poke(child, sw, user_rbs_end, addr, val); + if (ret < 0) + return ret; + } + return 0; +} + +typedef long (*syncfunc_t)(struct task_struct *, struct switch_stack *, + unsigned long, unsigned long); + +static void do_sync_rbs(struct unw_frame_info *info, void *arg) +{ + struct pt_regs *pt; + unsigned long urbs_end; + syncfunc_t fn = arg; + + if (unw_unwind_to_user(info) < 0) + return; + pt = task_pt_regs(info->task); + urbs_end = ia64_get_user_rbs_end(info->task, pt, NULL); + + fn(info->task, info->sw, pt->ar_bspstore, urbs_end); +} + +/* + * when a thread is stopped (ptraced), debugger might change thread's user + * stack (change memory directly), and we must avoid the RSE stored in kernel + * to override user stack (user space's RSE is newer than kernel's in the + * case). To workaround the issue, we copy kernel RSE to user RSE before the + * task is stopped, so user RSE has updated data. we then copy user RSE to + * kernel after the task is resummed from traced stop and kernel will use the + * newer RSE to return to user. TIF_RESTORE_RSE is the flag to indicate we need + * synchronize user RSE to kernel. + */ +void ia64_ptrace_stop(void) +{ + if (test_and_set_tsk_thread_flag(current, TIF_RESTORE_RSE)) + return; + tsk_set_notify_resume(current); + unw_init_running(do_sync_rbs, ia64_sync_user_rbs); +} + +/* + * This is called to read back the register backing store. + */ +void ia64_sync_krbs(void) +{ + clear_tsk_thread_flag(current, TIF_RESTORE_RSE); + tsk_clear_notify_resume(current); + + unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs); +} + +/* + * After PTRACE_ATTACH, a thread's register backing store area in user + * space is assumed to contain correct data whenever the thread is + * stopped. arch_ptrace_stop takes care of this on tracing stops. + * But if the child was already stopped for job control when we attach + * to it, then it might not ever get into ptrace_stop by the time we + * want to examine the user memory containing the RBS. + */ +void +ptrace_attach_sync_user_rbs (struct task_struct *child) +{ + int stopped = 0; + struct unw_frame_info info; + + /* + * If the child is in TASK_STOPPED, we need to change that to + * TASK_TRACED momentarily while we operate on it. This ensures + * that the child won't be woken up and return to user mode while + * we are doing the sync. (It can only be woken up for SIGKILL.) + */ + + read_lock(&tasklist_lock); + if (child->signal) { + spin_lock_irq(&child->sighand->siglock); + if (child->state == TASK_STOPPED && + !test_and_set_tsk_thread_flag(child, TIF_RESTORE_RSE)) { + tsk_set_notify_resume(child); + + child->state = TASK_TRACED; + stopped = 1; + } + spin_unlock_irq(&child->sighand->siglock); + } + read_unlock(&tasklist_lock); + + if (!stopped) + return; + + unw_init_from_blocked_task(&info, child); + do_sync_rbs(&info, ia64_sync_user_rbs); + + /* + * Now move the child back into TASK_STOPPED if it should be in a + * job control stop, so that SIGCONT can be used to wake it up. + */ + read_lock(&tasklist_lock); + if (child->signal) { + spin_lock_irq(&child->sighand->siglock); + if (child->state == TASK_TRACED && + (child->signal->flags & SIGNAL_STOP_STOPPED)) { + child->state = TASK_STOPPED; + } + spin_unlock_irq(&child->sighand->siglock); + } + read_unlock(&tasklist_lock); +} + static inline int thread_matches (struct task_struct *thread, unsigned long addr) { @@ -1422,6 +1545,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) struct task_struct *child; struct switch_stack *sw; long ret; + struct unw_frame_info info; lock_kernel(); ret = -EPERM; @@ -1453,6 +1577,8 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); + if (!ret) + arch_ptrace_attach(child); goto out_tsk; } @@ -1481,6 +1607,11 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) /* write the word at location addr */ urbs_end = ia64_get_user_rbs_end(child, pt, NULL); ret = ia64_poke(child, sw, urbs_end, addr, data); + + /* Make sure user RBS has the latest data */ + unw_init_from_blocked_task(&info, child); + do_sync_rbs(&info, ia64_sync_user_rbs); + goto out_tsk; case PTRACE_PEEKUSR: @@ -1634,6 +1765,10 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, && (current->ptrace & PT_PTRACED)) syscall_trace(); + /* copy user rbs to kernel rbs */ + if (test_thread_flag(TIF_RESTORE_RSE)) + ia64_sync_krbs(); + if (unlikely(current->audit_context)) { long syscall; int arch; @@ -1671,4 +1806,8 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, || test_thread_flag(TIF_SINGLESTEP)) && (current->ptrace & PT_PTRACED)) syscall_trace(); + + /* copy user rbs to kernel rbs */ + if (test_thread_flag(TIF_RESTORE_RSE)) + ia64_sync_krbs(); } diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 3ab04272097..17fda5293c6 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -49,13 +49,13 @@ EXPORT_SYMBOL(last_cli_ip); #endif static struct clocksource clocksource_itc = { - .name = "itc", - .rating = 350, - .read = itc_get_cycles, - .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /*to be caluclated*/ - .shift = 16, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .name = "itc", + .rating = 350, + .read = itc_get_cycles, + .mask = CLOCKSOURCE_MASK(64), + .mult = 0, /*to be calculated*/ + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static struct clocksource *itc_clocksource; diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 795180b8fd8..d4679ab55b9 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -35,6 +35,13 @@ config NO_IOPORT config NO_DMA def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + source "init/Kconfig" diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c index f1f5db0c408..0392112a5d7 100644 --- a/arch/m32r/kernel/setup.c +++ b/arch/m32r/kernel/setup.c @@ -369,7 +369,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index ffabd01c45e..2b0ed89cd17 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -52,6 +52,13 @@ config NO_IOPORT config NO_DMA def_bool SUN3 +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + mainmenu "Linux/68k Kernel Configuration" source "init/Kconfig" diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index f85b928ffac..5de4e4ed76a 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/reboot.h> #include <linux/init_task.h> #include <linux/mqueue.h> @@ -316,53 +315,6 @@ int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) EXPORT_SYMBOL(dump_fpu); /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - struct switch_stack *sw; - -/* changed the size calculations - should hopefully work better. lbt */ - dump->magic = CMAGIC; - dump->start_code = 0; - dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + - (PAGE_SIZE-1))) >> PAGE_SHIFT; - dump->u_dsize -= dump->u_tsize; - dump->u_ssize = 0; - - if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - - dump->u_ar0 = offsetof(struct user, regs); - sw = ((struct switch_stack *)regs) - 1; - dump->regs.d1 = regs->d1; - dump->regs.d2 = regs->d2; - dump->regs.d3 = regs->d3; - dump->regs.d4 = regs->d4; - dump->regs.d5 = regs->d5; - dump->regs.d6 = sw->d6; - dump->regs.d7 = sw->d7; - dump->regs.a0 = regs->a0; - dump->regs.a1 = regs->a1; - dump->regs.a2 = regs->a2; - dump->regs.a3 = sw->a3; - dump->regs.a4 = sw->a4; - dump->regs.a5 = sw->a5; - dump->regs.a6 = sw->a6; - dump->regs.d0 = regs->d0; - dump->regs.orig_d0 = regs->orig_d0; - dump->regs.stkadj = regs->stkadj; - dump->regs.sr = regs->sr; - dump->regs.pc = regs->pc; - dump->regs.fmtvec = (regs->format << 12) | regs->vector; - /* dump floating point stuff */ - dump->u_fpvalid = dump_fpu (regs, &dump->m68kfp); -} -EXPORT_SYMBOL(dump_thread); - -/* * sys_execve() executes a new program. */ asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp) diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index 9a06c48edcb..bba650312fd 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -450,7 +450,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos) static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 97f556fa493..fd4858e2dd6 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -23,7 +23,6 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/module.h> -#include <linux/a.out.h> #include <linux/user.h> #include <linux/string.h> #include <linux/linkage.h> diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 6abbbb8aac5..548a7b32163 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -64,6 +64,9 @@ config TIME_LOW_RES config NO_IOPORT def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" menu "Processor type and features" @@ -522,6 +525,11 @@ config 4KSTACKS running more threads on a system and also reduces the pressure on the VM subsystem for higher order allocations. +config HZ + int + default 1000 if CLEOPATRA + default 100 + comment "RAM configuration" config RAMBASE diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c index 156c6c662c7..d6f0200316f 100644 --- a/arch/m68knommu/kernel/setup.c +++ b/arch/m68knommu/kernel/setup.c @@ -260,7 +260,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index 197d7977de3..413bd1d37f5 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -338,8 +338,10 @@ void __init init_i8259_irqs(void) init_8259A(0); - for (i = I8259A_IRQ_BASE; i < I8259A_IRQ_BASE + 16; i++) + for (i = I8259A_IRQ_BASE; i < I8259A_IRQ_BASE + 16; i++) { set_irq_chip_and_handler(i, &i8259A_chip, handle_level_irq); + set_irq_probe(i); + } setup_irq(I8259A_IRQ_BASE + PIC_CASCADE_IR, &irq2); } diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 7852c7cdf29..290d8e3a664 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -591,9 +591,9 @@ static void irix_map_prda_page(void) return; pp = (struct prda *) v; - pp->prda_sys.t_pid = current->pid; + pp->prda_sys.t_pid = task_pid_vnr(current); pp->prda_sys.t_prid = read_c0_prid(); - pp->prda_sys.t_rpid = current->pid; + pp->prda_sys.t_rpid = task_pid_vnr(current); /* We leave the rest set to zero */ } @@ -1170,11 +1170,11 @@ static int irix_core_dump(long signr, struct pt_regs *regs, struct file *file, u prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; prstatus.pr_sigpend = current->pending.signal.sig[0]; prstatus.pr_sighold = current->blocked.sig[0]; - psinfo.pr_pid = prstatus.pr_pid = current->pid; - psinfo.pr_ppid = prstatus.pr_ppid = current->parent->pid; - psinfo.pr_pgrp = prstatus.pr_pgrp = task_pgrp_nr(current); - psinfo.pr_sid = prstatus.pr_sid = task_session_nr(current); - if (current->pid == current->tgid) { + psinfo.pr_pid = prstatus.pr_pid = task_pid_vnr(current); + psinfo.pr_ppid = prstatus.pr_ppid = task_pid_vnr(current->parent); + psinfo.pr_pgrp = prstatus.pr_pgrp = task_pgrp_vnr(current); + psinfo.pr_sid = prstatus.pr_sid = task_session_vnr(current); + if (thread_group_leader(current)) { /* * This is the record for the group leader. Add in the * cumulative times of previous dead threads. This total diff --git a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c index 5b10ac133ec..0215c805a59 100644 --- a/arch/mips/kernel/irixsig.c +++ b/arch/mips/kernel/irixsig.c @@ -578,10 +578,11 @@ out: #define W_MASK (W_EXITED | W_TRAPPED | W_STOPPED | W_CONT | W_NOHANG) -asmlinkage int irix_waitsys(int type, int pid, +asmlinkage int irix_waitsys(int type, int upid, struct irix5_siginfo __user *info, int options, struct rusage __user *ru) { + struct pid *pid = NULL; int flag, retval; DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; @@ -604,6 +605,8 @@ asmlinkage int irix_waitsys(int type, int pid, if (type != IRIX_P_PID && type != IRIX_P_PGID && type != IRIX_P_ALL) return -EINVAL; + if (type != IRIX_P_ALL) + pid = find_get_pid(upid); add_wait_queue(¤t->signal->wait_chldexit, &wait); repeat: flag = 0; @@ -612,9 +615,9 @@ repeat: tsk = current; list_for_each(_p, &tsk->children) { p = list_entry(_p, struct task_struct, sibling); - if ((type == IRIX_P_PID) && p->pid != pid) + if ((type == IRIX_P_PID) && task_pid(p) != pid) continue; - if ((type == IRIX_P_PGID) && task_pgrp_nr(p) != pid) + if ((type == IRIX_P_PGID) && task_pgrp(p) != pid) continue; if ((p->exit_signal != SIGCHLD)) continue; @@ -639,7 +642,7 @@ repeat: retval = __put_user(SIGCHLD, &info->sig); retval |= __put_user(0, &info->code); - retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user(task_pid_vnr(p), &info->stuff.procinfo.pid); retval |= __put_user((p->exit_code >> 8) & 0xff, &info->stuff.procinfo.procdata.child.status); retval |= __put_user(p->utime, &info->stuff.procinfo.procdata.child.utime); @@ -657,7 +660,7 @@ repeat: getrusage(p, RUSAGE_BOTH, ru); retval = __put_user(SIGCHLD, &info->sig); retval |= __put_user(1, &info->code); /* CLD_EXITED */ - retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user(task_pid_vnr(p), &info->stuff.procinfo.pid); retval |= __put_user((p->exit_code >> 8) & 0xff, &info->stuff.procinfo.procdata.child.status); retval |= __put_user(p->utime, @@ -665,7 +668,7 @@ repeat: retval |= __put_user(p->stime, &info->stuff.procinfo.procdata.child.stime); if (retval) - return retval; + goto end_waitsys; if (p->real_parent != p->parent) { write_lock_irq(&tasklist_lock); @@ -698,6 +701,7 @@ repeat: end_waitsys: current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit, &wait); + put_pid(pid); return retval; } diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index d06e9c9af79..e3309ff9ece 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -145,6 +145,11 @@ __setup("nokgdb", nokgdb); void __init init_IRQ(void) { + int i; + + for (i = 0; i < NR_IRQS; i++) + set_irq_noprobe(i); + arch_init_irq(); #ifdef CONFIG_KGDB diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 22fd41e946b..d70c4e0e85f 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -582,8 +582,8 @@ out: asmlinkage int irix_getpid(struct pt_regs *regs) { - regs->regs[3] = current->real_parent->pid; - return current->pid; + regs->regs[3] = task_pid_vnr(current->real_parent); + return task_pid_vnr(current); } asmlinkage int irix_getuid(struct pt_regs *regs) @@ -763,11 +763,11 @@ asmlinkage int irix_setpgrp(int flags) printk("[%s:%d] setpgrp(%d) ", current->comm, current->pid, flags); #endif if(!flags) - error = task_pgrp_nr(current); + error = task_pgrp_vnr(current); else error = sys_setsid(); #ifdef DEBUG_PROCGRPS - printk("returning %d\n", task_pgrp_nr(current)); + printk("returning %d\n", error); #endif return error; @@ -1093,10 +1093,10 @@ asmlinkage int irix_BSDsetpgrp(int pid, int pgrp) pid, pgrp); #endif if(!pid) - pid = current->pid; + pid = task_pid_vnr(current); /* Wheee, weird sysv thing... */ - if ((pgrp == 0) && (pid == current->pid)) + if ((pgrp == 0) && (pid == task_pid_vnr(current))) error = sys_setsid(); else error = sys_setpgid(pid, pgrp); diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig new file mode 100644 index 00000000000..eedc3a5e0d9 --- /dev/null +++ b/arch/mn10300/Kconfig @@ -0,0 +1,381 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +mainmenu "Linux Kernel Configuration" + +config MN10300 + def_bool y + +config AM33 + def_bool y + +config MMU + def_bool y + +config HIGHMEM + def_bool n + +config NUMA + def_bool n + +config UID16 + def_bool y + +config RWSEM_GENERIC_SPINLOCK + def_bool y + +config RWSEM_XCHGADD_ALGORITHM + bool + +config GENERIC_HARDIRQS_NO__DO_IRQ + def_bool y + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config GENERIC_FIND_NEXT_BIT + def_bool y + +config GENERIC_HWEIGHT + def_bool y + +config GENERIC_TIME + def_bool y + +config GENERIC_BUG + def_bool y + +config QUICKLIST + def_bool y + +config ARCH_HAS_ILOG2_U32 + def_bool y + +config ARCH_SUPPORTS_AOUT + def_bool n + +# Use the generic interrupt handling code in kernel/irq/ +config GENERIC_HARDIRQS + def_bool y + +config HOTPLUG_CPU + def_bool n + +mainmenu "Matsushita MN10300/AM33 Kernel Configuration" + +source "init/Kconfig" + + +menu "Matsushita MN10300 system setup" + +choice + prompt "Unit type" + default MN10300_UNIT_ASB2303 + help + This option specifies board for which the kernel will be + compiled. It affects the external peripherals catered for. + +config MN10300_UNIT_ASB2303 + bool "ASB2303" + +config MN10300_UNIT_ASB2305 + bool "ASB2305" + +endchoice + +choice + prompt "Processor support" + default MN10300_PROC_MN103E010 + help + This option specifies the processor for which the kernel will be + compiled. It affects the on-chip peripherals catered for. + +config MN10300_PROC_MN103E010 + bool "MN103E010" + depends on MN10300_UNIT_ASB2303 || MN10300_UNIT_ASB2305 + select MN10300_PROC_HAS_TTYSM0 + select MN10300_PROC_HAS_TTYSM1 + select MN10300_PROC_HAS_TTYSM2 + +endchoice + +choice + prompt "Processor core support" + default MN10300_CPU_AM33V2 + help + This option specifies the processor core for which the kernel will be + compiled. It affects the instruction set used. + +config MN10300_CPU_AM33V2 + bool "AM33v2" + +endchoice + +config FPU + bool "FPU present" + default y + depends on MN10300_PROC_MN103E010 + +choice + prompt "CPU Caching mode" + default MN10300_CACHE_WBACK + help + This option determines the caching mode for the kernel. + + Write-Back caching mode involves the all reads and writes causing + the affected cacheline to be read into the cache first before being + operated upon. Memory is not then updated by a write until the cache + is filled and a cacheline needs to be displaced from the cache to + make room. Only at that point is it written back. + + Write-Through caching only fetches cachelines from memory on a + read. Writes always get written directly to memory. If the affected + cacheline is also in cache, it will be updated too. + + The final option is to turn of caching entirely. + +config MN10300_CACHE_WBACK + bool "Write-Back" + +config MN10300_CACHE_WTHRU + bool "Write-Through" + +config MN10300_CACHE_DISABLED + bool "Disabled" + +endchoice + +menu "Memory layout options" + +config KERNEL_RAM_BASE_ADDRESS + hex "Base address of kernel RAM" + default "0x90000000" + +config INTERRUPT_VECTOR_BASE + hex "Base address of vector table" + default "0x90000000" + help + The base address of the vector table will be programmed into + the TBR register. It must be on 16MiB address boundary. + +config KERNEL_TEXT_ADDRESS + hex "Base address of kernel" + default "0x90001000" + +config KERNEL_ZIMAGE_BASE_ADDRESS + hex "Base address of compressed vmlinux image" + default "0x90700000" + +endmenu + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config PREEMPT_BKL + bool "Preempt The Big Kernel Lock" + depends on PREEMPT + default y + help + This option reduces the latency of the kernel by making the + big kernel lock preemptible. + + Say Y here if you are building a kernel for a desktop system. + Say N if you are unsure. + +config MN10300_CURRENT_IN_E2 + bool "Hold current task address in E2 register" + default y + help + This option removes the E2/R2 register from the set available to gcc + for normal use and instead uses it to store the address of the + current process's task_struct whilst in the kernel. + + This means the kernel doesn't need to calculate the address each time + "current" is used (take SP, AND with mask and dereference pointer + just to get the address), and instead can just use E2+offset + addressing each time. + + This has no effect on userspace. + +config MN10300_USING_JTAG + bool "Using JTAG to debug kernel" + default y + help + This options indicates that JTAG will be used to debug the kernel. It + suppresses the use of certain hardware debugging features, such as + single-stepping, which are taken over completely by the JTAG unit. + +config MN10300_RTC + bool "Using MN10300 RTC" + depends on MN10300_PROC_MN103E010 + default n + help + + This option enables support for the RTC, thus enabling time to be + tracked, even when system is powered down. This is available on-chip + on the MN103E010. + +config MN10300_WD_TIMER + bool "Using MN10300 watchdog timer" + default y + help + This options indicates that the watchdog timer will be used. + +config PCI + bool "Use PCI" + depends on MN10300_UNIT_ASB2305 + default y + help + Some systems (such as the ASB2305) have PCI onboard. If you have one + of these boards and you wish to use the PCI facilities, say Y here. + + The PCI-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +menu "MN10300 internal serial options" + +config MN10300_PROC_HAS_TTYSM0 + bool + default n + +config MN10300_PROC_HAS_TTYSM1 + bool + default n + +config MN10300_PROC_HAS_TTYSM2 + bool + default n + +config MN10300_TTYSM + bool "Support for ttySM serial ports" + depends on MN10300 + default y + select SERIAL_CORE + help + This option enables support for the on-chip serial ports that the + MN10300 has available. + +config MN10300_TTYSM_CONSOLE + bool "Support for console on ttySM serial ports" + depends on MN10300_TTYSM + select SERIAL_CORE_CONSOLE + help + This option enables support for a console on the on-chip serial ports + that the MN10300 has available. + +# +# /dev/ttySM0 +# +config MN10300_TTYSM0 + bool "Enable SIF0 (/dev/ttySM0)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM0 + help + Enable access to SIF0 through /dev/ttySM0 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF0" + default MN10300_TTYSM0_TIMER8 + depends on MN10300_TTYSM0 + +config MN10300_TTYSM0_TIMER8 + bool "Use timer 8 (16-bit)" + +config MN10300_TTYSM0_TIMER2 + bool "Use timer 2 (8-bit)" + +endchoice + +# +# /dev/ttySM1 +# +config MN10300_TTYSM1 + bool "Enable SIF1 (/dev/ttySM1)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM1 + help + Enable access to SIF1 through /dev/ttySM1 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF1" + default MN10300_TTYSM0_TIMER9 + depends on MN10300_TTYSM1 + +config MN10300_TTYSM1_TIMER9 + bool "Use timer 9 (16-bit)" + +config MN10300_TTYSM1_TIMER3 + bool "Use timer 3 (8-bit)" + +endchoice + +# +# /dev/ttySM2 +# +config MN10300_TTYSM2 + bool "Enable SIF2 (/dev/ttySM2)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM2 + help + Enable access to SIF2 through /dev/ttySM2 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF2" + default MN10300_TTYSM0_TIMER10 + depends on MN10300_TTYSM2 + +config MN10300_TTYSM2_TIMER10 + bool "Use timer 10 (16-bit)" + +endchoice + +config MN10300_TTYSM2_CTS + bool "Enable the use of the CTS line /dev/ttySM2" + depends on MN10300_TTYSM2 + +endmenu + +source "mm/Kconfig" + +menu "Power management options" +source kernel/power/Kconfig +endmenu + +endmenu + + +menu "Executable formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "net/Kconfig" + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/mn10300/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + +source "arch/mn10300/oprofile/Kconfig" diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug new file mode 100644 index 00000000000..524e33819f3 --- /dev/null +++ b/arch/mn10300/Kconfig.debug @@ -0,0 +1,135 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_DECOMPRESS_KERNEL + bool "Using serial port during decompressing kernel" + depends on DEBUG_KERNEL + default n + help + If you say Y here you will confirm the start and the end of + decompressing Linux seeing "Uncompressing Linux... " and + "Ok, booting the kernel.\n" on console. + +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +config GDBSTUB + bool "Remote GDB kernel debugging" + depends on DEBUG_KERNEL + select DEBUG_INFO + select FRAME_POINTER + help + If you say Y here, it will be possible to remotely debug the kernel + using gdb. This enlarges your kernel ELF image disk size by several + megabytes and requires a machine with more than 16 MB, better 32 MB + RAM to avoid excessive linking time. This is only useful for kernel + hackers. If unsure, say N. + +config GDBSTUB_IMMEDIATE + bool "Break into GDB stub immediately" + depends on GDBSTUB + help + If you say Y here, GDB stub will break into the program as soon as + possible, leaving the program counter at the beginning of + start_kernel() in init/main.c. + +config GDB_CONSOLE + bool "Console output to GDB" + depends on GDBSTUB + help + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + +config GDBSTUB_DEBUGGING + bool "Debug GDB stub by messages to serial port" + depends on GDBSTUB + help + This causes debugging messages to be displayed at various points + during execution of the GDB stub routines. Such messages will be + displayed on ttyS0 if that isn't the GDB stub's port, or ttySM0 + otherwise. + +config GDBSTUB_DEBUG_ENTRY + bool "Debug GDB stub entry" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about entry to or exit + from the main GDB stub routine. + +config GDBSTUB_DEBUG_PROTOCOL + bool "Debug GDB stub protocol" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about the GDB remote + protocol messages generated exchanged with GDB. + +config GDBSTUB_DEBUG_IO + bool "Debug GDB stub I/O" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + low-level I/O. + +config GDBSTUB_DEBUG_BREAKPOINT + bool "Debug GDB stub breakpoint management" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + breakpoint management. + +choice + prompt "GDB stub port" + default GDBSTUB_TTYSM0 + depends on GDBSTUB + help + Select the serial port used for GDB-stub. + +config GDBSTUB_ON_TTYSM0 + bool "/dev/ttySM0 [SIF0]" + depends on MN10300_TTYSM0 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM1 + bool "/dev/ttySM1 [SIF1]" + depends on MN10300_TTYSM1 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM2 + bool "/dev/ttySM2 [SIF2]" + depends on MN10300_TTYSM2 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYS0 + bool "/dev/ttyS0" + select GDBSTUB_ON_TTYSx + +config GDBSTUB_ON_TTYS1 + bool "/dev/ttyS1" + select GDBSTUB_ON_TTYSx + +endchoice + +config GDBSTUB_ON_TTYSMx + bool + depends on GDBSTUB_ON_TTYSM0 || GDBSTUB_ON_TTYSM1 || GDBSTUB_ON_TTYSM2 + default y + +config GDBSTUB_ON_TTYSx + bool + depends on GDBSTUB_ON_TTYS0 || GDBSTUB_ON_TTYS1 + default y + +endmenu diff --git a/arch/mn10300/Makefile b/arch/mn10300/Makefile new file mode 100644 index 00000000000..6673a28ec07 --- /dev/null +++ b/arch/mn10300/Makefile @@ -0,0 +1,135 @@ +############################################################################### +# +# MN10300 Kernel makefile system specifications +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### + +KBUILD_DEFCONFIG := asb2303_defconfig + +CCSPECS := $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-) +CCDIR := $(strip $(patsubst %/specs,%,$(CCSPECS))) +KBUILD_CPPFLAGS += -nostdinc -I$(CCDIR)/include + +LDFLAGS := +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +#LDFLAGS_vmlinux := -Map linkmap.txt +CHECKFLAGS += + +PROCESSOR := unset +UNIT := unset + +KBUILD_CFLAGS += -mam33 -mmem-funcs -DCPU=AM33 +KBUILD_AFLAGS += -mam33 -DCPU=AM33 + +ifeq ($(CONFIG_MN10300_CURRENT_IN_E2),y) +KBUILD_CFLAGS += -ffixed-e2 -fcall-saved-e5 +endif + +ifeq ($(CONFIG_MN10300_PROC_MN103E010),y) +PROCESSOR := mn103e010 +endif + +ifeq ($(CONFIG_MN10300_UNIT_ASB2303),y) +UNIT := asb2303 +endif +ifeq ($(CONFIG_MN10300_UNIT_ASB2305),y) +UNIT := asb2305 +endif + + +head-y := arch/mn10300/kernel/head.o arch/mn10300/kernel/init_task.o + +core-y += arch/mn10300/kernel/ arch/mn10300/mm/ + +ifneq ($(PROCESSOR),unset) +core-y += arch/mn10300/proc-$(PROCESSOR)/ +endif +ifneq ($(UNIT),unset) +core-y += arch/mn10300/unit-$(UNIT)/ +endif +libs-y += arch/mn10300/lib/ + +drivers-$(CONFIG_OPROFILE) += arch/mn10300/oprofile/ + +boot := arch/mn10300/boot + +.PHONY: zImage + +KBUILD_IMAGE := $(boot)/zImage +CLEAN_FILES += $(boot)/zImage +CLEAN_FILES += $(boot)/compressed/vmlinux +CLEAN_FILES += $(boot)/compressed/vmlinux.bin +CLEAN_FILES += $(boot)/compressed/vmlinux.bin.gz + +zImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +all: zImage + +bootstrap: + $(Q)$(MAKEBOOT) bootstrap + +archclean: + $(Q)$(MAKE) $(clean)=arch/mn10300/proc-mn103e010 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2303 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2305 + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' +endef + +# If you make sure the .S files get compiled with debug info, +# uncomment the following to disable optimisations +# that are unhelpful whilst debugging. +ifdef CONFIG_DEBUG_INFO +#KBUILD_CFLAGS += -O1 +KBUILD_AFLAGS += -Wa,--gdwarf2 +endif + +################################################################################################### +# +# juggle some symlinks in the MN10300 asm include dir +# +# Update machine proc and unit symlinks if something which affects +# them changed. We use .proc / .unit to indicate when they were +# updated last, otherwise make uses the target directory mtime. +# +################################################################################################### + +# processor specific definitions +include/asm-mn10300/.proc: $(wildcard include/config/proc/*.h) include/config/auto.conf + @echo ' SYMLINK include/asm-mn10300/proc -> include/asm-mn10300/proc-$(PROCESSOR)' +ifneq ($(KBUILD_SRC),) + $(Q)mkdir -p include/asm-mn10300 + $(Q)ln -fsn $(srctree)/include/asm-mn10300/proc-$(PROCESSOR) include/asm-mn10300/proc +else + $(Q)ln -fsn proc-$(PROCESSOR) include/asm-mn10300/proc +endif + @touch $@ + +CLEAN_FILES += include/asm-mn10300/proc include/asm-mn10300/.proc + +prepare: include/asm-mn10300/.proc + +# unit specific definitions +include/asm-mn10300/.unit: $(wildcard include/config/unit/*.h) include/config/auto.conf + @echo ' SYMLINK include/asm-mn10300/unit -> include/asm-mn10300/unit-$(UNIT)' +ifneq ($(KBUILD_SRC),) + $(Q)mkdir -p include/asm-mn10300 + $(Q)ln -fsn $(srctree)/include/asm-mn10300/unit-$(UNIT) include/asm-mn10300/unit +else + $(Q)ln -fsn unit-$(UNIT) include/asm-mn10300/unit +endif + @touch $@ + +CLEAN_FILES += include/asm-mn10300/unit include/asm-mn10300/.unit + +prepare: include/asm-mn10300/.unit diff --git a/arch/mn10300/boot/.gitignore b/arch/mn10300/boot/.gitignore new file mode 100644 index 00000000000..b6718de2369 --- /dev/null +++ b/arch/mn10300/boot/.gitignore @@ -0,0 +1 @@ +zImage diff --git a/arch/mn10300/boot/Makefile b/arch/mn10300/boot/Makefile new file mode 100644 index 00000000000..36c9caf8ea0 --- /dev/null +++ b/arch/mn10300/boot/Makefile @@ -0,0 +1,28 @@ +# MN10300 kernel compressor and wrapper +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +targets := vmlinux.bin zImage + +subdir- := compressed + +# --------------------------------------------------------------------------- + + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @echo 'Kernel: $@ is ready' + +$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ diff --git a/arch/mn10300/boot/compressed/Makefile b/arch/mn10300/boot/compressed/Makefile new file mode 100644 index 00000000000..08a95e17168 --- /dev/null +++ b/arch/mn10300/boot/compressed/Makefile @@ -0,0 +1,22 @@ +# +# Create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o + +LDFLAGS_vmlinux := -Ttext $(CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS) -e startup_32 + +$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-am33lin -T + +$(obj)/piggy.o: $(obj)/vmlinux.lds $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,ld) diff --git a/arch/mn10300/boot/compressed/head.S b/arch/mn10300/boot/compressed/head.S new file mode 100644 index 00000000000..502e1eb5670 --- /dev/null +++ b/arch/mn10300/boot/compressed/head.S @@ -0,0 +1,86 @@ +/* Boot entry point for a compressed MN10300 kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .section .text + +#define DEBUG + +#include <linux/linkage.h> +#include <asm/cpu-regs.h> + + .globl startup_32 +startup_32: + # first save off parameters from bootloader + mov param_save_area,a0 + mov d0,(a0) + mov d1,(4,a0) + mov d2,(8,a0) + + mov sp,a3 + mov decomp_stack+0x2000-4,a0 + mov a0,sp + + # invalidate and enable both of the caches + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD,d0 # writethru dcache + movhu d0,(a0) # enable + + # clear the BSS area + mov __bss_start,a0 + mov _end,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + movbu d0,(a0) + inc a0 + bra bssclear +bssclear_end: + + # decompress the kernel + call decompress_kernel[],0 + + # disable caches again + mov CHCTR,a0 + clr d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + + mov param_save_area,a0 + mov (a0),d0 + mov (4,a0),d1 + mov (8,a0),d2 + + mov a3,sp + mov CONFIG_KERNEL_TEXT_ADDRESS,a0 + jmp (a0) + + .data + .align 4 +param_save_area: + .rept 3 + .word 0 + .endr + + .section .bss + .align 4 +decomp_stack: + .space 0x2000 diff --git a/arch/mn10300/boot/compressed/misc.c b/arch/mn10300/boot/compressed/misc.c new file mode 100644 index 00000000000..ded207efc97 --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.c @@ -0,0 +1,429 @@ +/* MN10300 Miscellaneous helper routines for kernel decompressor + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * - Derived from arch/x86/boot/compressed/misc_32.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/compiler.h> +#include <asm/serial-regs.h> +#include "misc.h" + +#ifndef CONFIG_GDBSTUB_ON_TTYSx +/* display 'Uncompressing Linux... ' messages on ttyS0 or ttyS1 */ +#if 1 /* ttyS0 */ +#define CYG_DEV_BASE 0xA6FB0000 +#else /* ttyS1 */ +#define CYG_DEV_BASE 0xA6FC0000 +#endif + +#define CYG_DEV_THR (*((volatile __u8*)(CYG_DEV_BASE + 0x00))) +#define CYG_DEV_MCR (*((volatile __u8*)(CYG_DEV_BASE + 0x10))) +#define SIO_MCR_DTR 0x01 +#define SIO_MCR_RTS 0x02 +#define CYG_DEV_LSR (*((volatile __u8*)(CYG_DEV_BASE + 0x14))) +#define SIO_LSR_THRE 0x20 /* transmitter holding register empty */ +#define SIO_LSR_TEMT 0x40 /* transmitter register empty */ +#define CYG_DEV_MSR (*((volatile __u8*)(CYG_DEV_BASE + 0x18))) +#define SIO_MSR_CTS 0x10 /* clear to send */ +#define SIO_MSR_DSR 0x20 /* data set ready */ + +#define LSR_WAIT_FOR(STATE) \ + do { while (!(CYG_DEV_LSR & SIO_LSR_##STATE)) {} } while (0) +#define FLOWCTL_QUERY(LINE) \ + ({ CYG_DEV_MSR & SIO_MSR_##LINE; }) +#define FLOWCTL_WAIT_FOR(LINE) \ + do { while (!(CYG_DEV_MSR & SIO_MSR_##LINE)) {} } while (0) +#define FLOWCTL_CLEAR(LINE) \ + do { CYG_DEV_MCR &= ~SIO_MCR_##LINE; } while (0) +#define FLOWCTL_SET(LINE) \ + do { CYG_DEV_MCR |= SIO_MCR_##LINE; } while (0) +#endif + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy + +static inline void *memset(const void *s, int c, size_t n) +{ + int i; + char *ss = (char *) s; + + for (i = 0; i < n; i++) + ss[i] = c; + return (void *)s; +} + +#define memzero(s, n) memset((s), 0, (n)) + +static inline void *memcpy(void *__dest, const void *__src, size_t __n) +{ + int i; + const char *s = __src; + char *d = __dest; + + for (i = 0; i < __n; i++) + d[i] = s[i]; + return __dest; +} + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, and a power of + * two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond, msg) { if (!(cond)) error(msg); } +# define Trace(x) fprintf x +# define Tracev(x) { if (verbose) fprintf x ; } +# define Tracevv(x) { if (verbose > 1) fprintf x ; } +# define Tracec(c, x) { if (verbose && (c)) fprintf x ; } +# define Tracecv(c, x) { if (verbose > 1 && (c)) fprintf x ; } +#else +# define Assert(cond, msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c, x) +# define Tracecv(c, x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(const char *) __attribute__((noreturn)); +static void kputs(const char *); + +static inline unsigned char get_byte(void) +{ + unsigned char ch = inptr < insize ? inbuf[inptr++] : fill_inbuf(); + +#if 0 + char hex[3]; + hex[0] = ((ch & 0x0f) > 9) ? + ((ch & 0x0f) + 'A' - 0xa) : ((ch & 0x0f) + '0'); + hex[1] = ((ch >> 4) > 9) ? + ((ch >> 4) + 'A' - 0xa) : ((ch >> 4) + '0'); + hex[2] = 0; + kputs(hex); +#endif + return ch; +} + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) 0x901e0) +#endif +#define SCREEN_INFO (*(struct screen_info *)0x90000) + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + + +static void *malloc(int size); + +static inline void free(void *where) +{ /* Don't care */ +} + +static unsigned long free_mem_ptr = (unsigned long) &end; +static unsigned long free_mem_end_ptr = (unsigned long) &end + 0x90000; + +static inline void gzip_mark(void **ptr) +{ + kputs("."); + *ptr = (void *) free_mem_ptr; +} + +static inline void gzip_release(void **ptr) +{ + free_mem_ptr = (unsigned long) *ptr; +} + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_END 0x90000 +#define LOW_BUFFER_SIZE (LOW_BUFFER_END - LOW_BUFFER_START) +#define HEAP_SIZE 0x3000 +static int high_loaded; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int lines, cols; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size < 0) + error("Malloc error\n"); + if (!free_mem_ptr) + error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *) free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static inline void scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); + for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) + vidmem[i] = ' '; +} + +static inline void kputchar(unsigned char ch) +{ +#ifdef CONFIG_MN10300_UNIT_ASB2305 + while (SC0STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & SC01STR_TBF) + continue; + } + + SC0TXB = ch; + +#else + while (SC1STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & SC01STR_TBF) + continue; + } + + SC1TXB = ch; + +#endif +} + +static void kputs(const char *s) +{ +#ifdef CONFIG_DEBUG_DECOMPRESS_KERNEL +#ifndef CONFIG_GDBSTUB_ON_TTYSx + char ch; + + FLOWCTL_SET(DTR); + + while (*s) { + LSR_WAIT_FOR(THRE); + + ch = *s++; + if (ch == 0x0a) { + CYG_DEV_THR = 0x0d; + LSR_WAIT_FOR(THRE); + } + CYG_DEV_THR = ch; + } + + FLOWCTL_CLEAR(DTR); +#else + + for (; *s; s++) + kputchar(*s); + +#endif +#endif /* CONFIG_DEBUG_DECOMPRESS_KERNEL */ +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +static int fill_inbuf() +{ + if (insize != 0) + error("ran out of input data\n"); + + inbuf = input_data; + insize = input_len; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window_low(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window_high(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg) output_data == LOW_BUFFER_END) + output_data = high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window(void) +{ + if (high_loaded) + flush_window_high(); + else + flush_window_low(); +} + +static void error(const char *x) +{ + kputs("\n\n"); + kputs(x); + kputs("\n\n -- System halted"); + + while (1) + /* Halt */; +} + +#define STACK_SIZE (4096) + +long user_stack[STACK_SIZE]; + +struct { + long *a; + short b; +} stack_start = { &user_stack[STACK_SIZE], 0 }; + +void setup_normal_output_buffer(void) +{ +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < 1024) + error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) + error("Less than 2MB of memory.\n"); +#endif + output_data = (char *) 0x100000; /* Points to 1M */ +} + +struct moveparams { + uch *low_buffer_start; + int lcount; + uch *high_buffer_start; + int hcount; +}; + +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg) &end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#endif + mv->low_buffer_start = output_data = (char *) LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long) high_buffer_start; + if (0x100000 + LOW_BUFFER_SIZE > (ulg) high_buffer_start) { + high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } else { + mv->hcount = -1; + } + mv->high_buffer_start = high_buffer_start; +} + +void close_output_buffer_if_we_run_high(struct moveparams *mv) +{ + mv->lcount = bytes_out; + if (bytes_out > LOW_BUFFER_SIZE) { + mv->lcount = LOW_BUFFER_SIZE; + if (mv->hcount) + mv->hcount = bytes_out - LOW_BUFFER_SIZE; + } else { + mv->hcount = 0; + } +} + +#undef DEBUGFLAG +#ifdef DEBUGFLAG +int debugflag; +#endif + +int decompress_kernel(struct moveparams *mv) +{ +#ifdef DEBUGFLAG + while (!debugflag) + barrier(); +#endif + + output_data = (char *) CONFIG_KERNEL_TEXT_ADDRESS; + + makecrc(); + kputs("Uncompressing Linux... "); + gunzip(); + kputs("Ok, booting the kernel.\n"); + return 0; +} diff --git a/arch/mn10300/boot/compressed/misc.h b/arch/mn10300/boot/compressed/misc.h new file mode 100644 index 00000000000..da921cd172f --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.h @@ -0,0 +1,18 @@ +/* Internal definitions for the MN10300 kernel decompressor + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +extern int end; + +/* + * vmlinux.lds + */ +extern char input_data[]; +extern int input_len; diff --git a/arch/mn10300/boot/compressed/vmlinux.lds b/arch/mn10300/boot/compressed/vmlinux.lds new file mode 100644 index 00000000000..a084903603f --- /dev/null +++ b/arch/mn10300/boot/compressed/vmlinux.lds @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/mn10300/boot/install.sh b/arch/mn10300/boot/install.sh new file mode 100644 index 00000000000..072951c8397 --- /dev/null +++ b/arch/mn10300/boot/install.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# arch/mn10300/boot/install -c.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# Licence. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install -c" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install -c path (blank if root directory) +# $5 - boot rom file +# + +# User may have a custom install -c script + +rm -fr $4/../usr/include/linux $4/../usr/include/asm +install -c -m 0755 $2 $4/vmlinuz +install -c -m 0755 $5 $4/boot.rom +install -c -m 0755 -d $4/../usr/include/linux +cd $TOPDIR/include/linux +for i in `find . -maxdepth 1 -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux +done +install -c -m 0755 -d $4/../usr/include/linux/byteorder +cd $TOPDIR/include/linux/byteorder +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/byteorder +done +install -c -m 0755 -d $4/../usr/include/linux/lockd +cd $TOPDIR/include/linux/lockd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/lockd +done +install -c -m 0755 -d $4/../usr/include/linux/netfilter_ipv4 +cd $TOPDIR/include/linux/netfilter_ipv4 +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/netfilter_ipv4 +done +install -c -m 0755 -d $4/../usr/include/linux/nfsd +cd $TOPDIR/include/linux/nfsd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/nfsd/$i +done +install -c -m 0755 -d $4/../usr/include/linux/raid +cd $TOPDIR/include/linux/raid +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/raid +done +install -c -m 0755 -d $4/../usr/include/linux/sunrpc +cd $TOPDIR/include/linux/sunrpc +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/sunrpc +done +install -c -m 0755 -d $4/../usr/include/asm +cd $TOPDIR/include/asm +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/asm +done diff --git a/arch/mn10300/boot/tools/build.c b/arch/mn10300/boot/tools/build.c new file mode 100644 index 00000000000..4f552ead0f8 --- /dev/null +++ b/arch/mn10300/boot/tools/build.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + * Rewritten by Martin Mares, April 1997 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> +#include <fcntl.h> +#include <asm/boot.h> + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* Minimal number of setup sectors (see also bootsect.S) */ +#define SETUP_SECTS 4 + +uint8_t buf[1024]; +int fd; +int is_big_kernel; + +__attribute__((noreturn)) +void die(const char *str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(stderr, str, args); + fputc('\n', stderr); + exit(1); +} + +void file_open(const char *name) +{ + fd = open(name, O_RDONLY, 0); + if (fd < 0) + die("Unable to open `%s': %m", name); +} + +__attribute__((noreturn)) +void usage(void) +{ + die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char **argv) +{ + unsigned int i, c, sz, setup_sectors; + uint32_t sys_size; + uint8_t major_root, minor_root; + struct stat sb; + + if (argc > 2 && !strcmp(argv[1], "-b")) { + is_big_kernel = 1; + argc--, argv++; + } + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + + file_open(argv[1]); + i = read(fd, buf, sizeof(buf)); + fprintf(stderr, "Boot sector %d bytes.\n", i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if (buf[510] != 0x55 || buf[511] != 0xaa) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = minor_root; + buf[509] = major_root; + if (write(1, buf, 512) != 512) + die("Write call failed"); + close(fd); + + /* Copy the setup code */ + file_open(argv[2]); + for (i = 0; (c = read(fd, buf, sizeof(buf))) > 0; i += c) + if (write(1, buf, c) != c) + die("Write call failed"); + if (c != 0) + die("read-error on `setup'"); + close(fd); + + /* Pad unused space with zeros */ + setup_sectors = (i + 511) / 512; + /* for compatibility with ancient versions of LILO. */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr, "Setup is %d bytes.\n", i); + memset(buf, 0, sizeof(buf)); + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1, buf, c) != c) + die("Write call failed"); + i += c; + } + + file_open(argv[3]); + if (fstat(fd, &sb)) + die("Unable to stat `%s': %m", argv[3]); + sz = sb.st_size; + fprintf(stderr, "System is %d kB\n", sz / 1024); + sys_size = (sz + 15) / 16; + /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ + if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE)) + die("System is too big. Try using %smodules.", + is_big_kernel ? "" : "bzImage or "); + if (sys_size > 0xffff) + fprintf(stderr, + "warning: kernel is too big for standalone boot " + "from floppy\n"); + while (sz > 0) { + int l, n; + + l = (sz > sizeof(buf)) ? sizeof(buf) : sz; + n = read(fd, buf, l); + if (n != l) { + if (n < 0) + die("Error reading %s: %m", argv[3]); + else + die("%s: Unexpected EOF", argv[3]); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(fd); + + /* Write sizes to the bootsector */ + if (lseek(1, 497, SEEK_SET) != 497) + die("Output: seek failed"); + buf[0] = setup_sectors; + if (write(1, buf, 1) != 1) + die("Write of setup sector count failed"); + if (lseek(1, 500, SEEK_SET) != 500) + die("Output: seek failed"); + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write of image length failed"); + + return 0; +} diff --git a/arch/mn10300/configs/asb2303_defconfig b/arch/mn10300/configs/asb2303_defconfig new file mode 100644 index 00000000000..ca9876a111d --- /dev/null +++ b/arch/mn10300/configs/asb2303_defconfig @@ -0,0 +1,558 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc2 +# Fri Nov 16 13:36:38 2007 +# +CONFIG_MN10300=y +CONFIG_AM33=y +CONFIG_MMU=y +# CONFIG_HIGHMEM is not set +# CONFIG_NUMA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_BUG=y +CONFIG_QUICKLIST=y +CONFIG_ARCH_HAS_ILOG2_U32=y +# CONFIG_ARCH_SUPPORTS_AOUT is not set +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_HOTPLUG_CPU is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +# CONFIG_BLOCK is not set + +# +# Matsushita MN10300 system setup +# +CONFIG_MN10300_UNIT_ASB2303=y +# CONFIG_MN10300_UNIT_ASB2305 is not set +CONFIG_MN10300_PROC_MN103E010=y +CONFIG_MN10300_CPU_AM33V2=y +CONFIG_FPU=y +CONFIG_MN10300_CACHE_WBACK=y +# CONFIG_MN10300_CACHE_WTHRU is not set +# CONFIG_MN10300_CACHE_DISABLED is not set + +# +# Memory layout options +# +CONFIG_KERNEL_RAM_BASE_ADDRESS=0x90000000 +CONFIG_INTERRUPT_VECTOR_BASE=0x90000000 +CONFIG_KERNEL_TEXT_ADDRESS=0x90001000 +CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS=0x90700000 +CONFIG_PREEMPT=y +CONFIG_PREEMPT_BKL=y +CONFIG_MN10300_CURRENT_IN_E2=y +CONFIG_MN10300_USING_JTAG=y +CONFIG_MN10300_RTC=y +CONFIG_MN10300_WD_TIMER=y +# CONFIG_ARCH_SUPPORTS_MSI is not set + +# +# MN10300 internal serial options +# +CONFIG_MN10300_PROC_HAS_TTYSM0=y +CONFIG_MN10300_PROC_HAS_TTYSM1=y +CONFIG_MN10300_PROC_HAS_TTYSM2=y +CONFIG_MN10300_TTYSM=y +CONFIG_MN10300_TTYSM_CONSOLE=y +CONFIG_MN10300_TTYSM0=y +CONFIG_MN10300_TTYSM0_TIMER8=y +# CONFIG_MN10300_TTYSM0_TIMER2 is not set +CONFIG_MN10300_TTYSM1=y +CONFIG_MN10300_TTYSM1_TIMER9=y +# CONFIG_MN10300_TTYSM1_TIMER3 is not set +# CONFIG_MN10300_TTYSM2 is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_NR_QUICK=1 +CONFIG_VIRT_TO_BUS=y + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Executable formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_DEBUG_VERBOSE=0 +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +CONFIG_MTD_CFI_I4=y +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0x8000000 +CONFIG_MTD_PHYSMAP_LEN=0x0 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set + +# +# SCSI device support +# +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +# CONFIG_SERIAL_8250_MANY_PORTS is not set +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +CONFIG_RTC=y +# CONFIG_R3964 is not set +# CONFIG_TCG_TPM is not set +# CONFIG_I2C is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_RTC_CLASS is not set + +# +# Userspace I/O +# +# CONFIG_UIO is not set + +# +# File systems +# +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile new file mode 100644 index 00000000000..ef07c956170 --- /dev/null +++ b/arch/mn10300/kernel/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the MN10300-specific core kernel code +# +extra-y := head.o init_task.o vmlinux.lds + +obj-y := process.o semaphore.o signal.o entry.o fpu.o traps.o irq.o \ + ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \ + switch_to.o mn10300_ksyms.o kernel_execve.o + +obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o + +obj-$(CONFIG_FPU) += fpu-low.o + +obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \ + mn10300-debug.o +obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o + +ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y) +obj-$(CONFIG_GDBSTUB) += gdb-cache.o +endif + +obj-$(CONFIG_MN10300_RTC) += rtc.o +obj-$(CONFIG_PROFILE) += profile.o profile-low.o +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KPROBES) += kprobes.o diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c new file mode 100644 index 00000000000..ee2d9f8af5a --- /dev/null +++ b/arch/mn10300/kernel/asm-offsets.c @@ -0,0 +1,108 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed + * to extract and format the required data. + */ + +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/personality.h> +#include <asm/ucontext.h> +#include <asm/processor.h> +#include <asm/thread_info.h> +#include <asm/ptrace.h> +#include "sigframe.h" +#include "mn10300-serial.h" + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->") + +#define OFFSET(sym, str, mem) \ + DEFINE(sym, offsetof(struct str, mem)); + +void foo(void) +{ + OFFSET(SIGCONTEXT_d0, sigcontext, d0); + OFFSET(SIGCONTEXT_d1, sigcontext, d1); + BLANK(); + + OFFSET(TI_task, thread_info, task); + OFFSET(TI_exec_domain, thread_info, exec_domain); + OFFSET(TI_flags, thread_info, flags); + OFFSET(TI_cpu, thread_info, cpu); + OFFSET(TI_preempt_count, thread_info, preempt_count); + OFFSET(TI_addr_limit, thread_info, addr_limit); + OFFSET(TI_restart_block, thread_info, restart_block); + BLANK(); + + OFFSET(REG_D0, pt_regs, d0); + OFFSET(REG_D1, pt_regs, d1); + OFFSET(REG_D2, pt_regs, d2); + OFFSET(REG_D3, pt_regs, d3); + OFFSET(REG_A0, pt_regs, a0); + OFFSET(REG_A1, pt_regs, a1); + OFFSET(REG_A2, pt_regs, a2); + OFFSET(REG_A3, pt_regs, a3); + OFFSET(REG_E0, pt_regs, e0); + OFFSET(REG_E1, pt_regs, e1); + OFFSET(REG_E2, pt_regs, e2); + OFFSET(REG_E3, pt_regs, e3); + OFFSET(REG_E4, pt_regs, e4); + OFFSET(REG_E5, pt_regs, e5); + OFFSET(REG_E6, pt_regs, e6); + OFFSET(REG_E7, pt_regs, e7); + OFFSET(REG_SP, pt_regs, sp); + OFFSET(REG_EPSW, pt_regs, epsw); + OFFSET(REG_PC, pt_regs, pc); + OFFSET(REG_LAR, pt_regs, lar); + OFFSET(REG_LIR, pt_regs, lir); + OFFSET(REG_MDR, pt_regs, mdr); + OFFSET(REG_MCVF, pt_regs, mcvf); + OFFSET(REG_MCRL, pt_regs, mcrl); + OFFSET(REG_MCRH, pt_regs, mcrh); + OFFSET(REG_MDRQ, pt_regs, mdrq); + OFFSET(REG_ORIG_D0, pt_regs, orig_d0); + OFFSET(REG_NEXT, pt_regs, next); + DEFINE(REG__END, sizeof(struct pt_regs)); + BLANK(); + + OFFSET(THREAD_UREGS, thread_struct, uregs); + OFFSET(THREAD_PC, thread_struct, pc); + OFFSET(THREAD_SP, thread_struct, sp); + OFFSET(THREAD_A3, thread_struct, a3); + OFFSET(THREAD_USP, thread_struct, usp); + OFFSET(THREAD_FRAME, thread_struct, __frame); + BLANK(); + + DEFINE(CLONE_VM_asm, CLONE_VM); + DEFINE(CLONE_FS_asm, CLONE_FS); + DEFINE(CLONE_FILES_asm, CLONE_FILES); + DEFINE(CLONE_SIGHAND_asm, CLONE_SIGHAND); + DEFINE(CLONE_UNTRACED_asm, CLONE_UNTRACED); + DEFINE(SIGCHLD_asm, SIGCHLD); + BLANK(); + + OFFSET(EXEC_DOMAIN_handler, exec_domain, handler); + OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext); + + DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + + OFFSET(__rx_buffer, mn10300_serial_port, rx_buffer); + OFFSET(__rx_inp, mn10300_serial_port, rx_inp); + OFFSET(__rx_outp, mn10300_serial_port, rx_outp); + OFFSET(__tx_info_buffer, mn10300_serial_port, uart.info); + OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar); + OFFSET(__tx_break, mn10300_serial_port, tx_break); + OFFSET(__intr_flags, mn10300_serial_port, intr_flags); + OFFSET(__rx_icr, mn10300_serial_port, rx_icr); + OFFSET(__tx_icr, mn10300_serial_port, tx_icr); + OFFSET(__tm_icr, mn10300_serial_port, _tmicr); + OFFSET(__iobase, mn10300_serial_port, _iobase); + + DEFINE(__UART_XMIT_SIZE, UART_XMIT_SIZE); + OFFSET(__xmit_buffer, uart_info, xmit.buf); + OFFSET(__xmit_head, uart_info, xmit.head); + OFFSET(__xmit_tail, uart_info, xmit.tail); +} diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S new file mode 100644 index 00000000000..11de3606eee --- /dev/null +++ b/arch/mn10300/kernel/entry.S @@ -0,0 +1,721 @@ +############################################################################### +# +# MN10300 Exception and interrupt entry points +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/system.h> +#include <asm/thread_info.h> +#include <asm/intctl-regs.h> +#include <asm/busctl-regs.h> +#include <asm/timer-regs.h> +#include <asm/unit/leds.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/errno.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + +#ifdef CONFIG_PREEMPT +#define preempt_stop __cli +#else +#define preempt_stop +#define resume_kernel restore_all +#endif + + .macro __cli + and ~EPSW_IM,epsw + or EPSW_IE|MN10300_CLI_LEVEL,epsw + nop + nop + nop + .endm + .macro __sti + or EPSW_IE|EPSW_IM_7,epsw + .endm + + + .am33_2 + +############################################################################### +# +# the return path for a forked child +# - on entry, D0 holds the address of the previous task to run +# +############################################################################### +ENTRY(ret_from_fork) + call schedule_tail[],0 + GET_THREAD_INFO a2 + + # return 0 to indicate child process + clr d0 + mov d0,(REG_D0,fp) + jmp syscall_exit + +############################################################################### +# +# system call handler +# +############################################################################### +ENTRY(system_call) + add -4,sp + SAVE_ALL + mov d0,(REG_ORIG_D0,fp) + GET_THREAD_INFO a2 + cmp nr_syscalls,d0 + bcc syscall_badsys + btst _TIF_SYSCALL_TRACE,(TI_flags,a2) + bne syscall_trace_entry +syscall_call: + add d0,d0,a1 + add a1,a1 + mov (REG_A0,fp),d0 + mov (sys_call_table,a1),a0 + calls (a0) + mov d0,(REG_D0,fp) +syscall_exit: + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + mov (TI_flags,a2),d2 + btst _TIF_ALLWORK_MASK,d2 + bne syscall_exit_work +restore_all: + RESTORE_ALL + +############################################################################### +# +# perform work that needs to be done immediately before resumption and syscall +# tracing +# +############################################################################### + ALIGN +syscall_exit_work: + btst _TIF_SYSCALL_TRACE,d2 + beq work_pending + __sti # could let do_syscall_trace() call + # schedule() instead + mov fp,d0 + mov 1,d1 + call do_syscall_trace[],0 # do_syscall_trace(regs,entryexit) + jmp resume_userspace + + ALIGN +work_pending: + btst _TIF_NEED_RESCHED,d2 + beq work_notifysig + +work_resched: + call schedule[],0 + + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + + # is there any work to be done other than syscall tracing? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + beq restore_all + btst _TIF_NEED_RESCHED,d2 + bne work_resched + + # deal with pending signals and notify-resume requests +work_notifysig: + mov fp,d0 + mov d2,d1 + call do_notify_resume[],0 + jmp resume_userspace + + # perform syscall entry tracing +syscall_trace_entry: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + mov fp,d0 + clr d1 + call do_syscall_trace[],0 + mov (REG_ORIG_D0,fp),d0 + mov (REG_D1,fp),d1 + cmp nr_syscalls,d0 + bcs syscall_call + jmp syscall_exit + +syscall_badsys: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + jmp resume_userspace + + # userspace resumption stub bypassing syscall exit tracing + .globl ret_from_exception, ret_from_intr + ALIGN +ret_from_exception: + preempt_stop +ret_from_intr: + GET_THREAD_INFO a2 + mov (REG_EPSW,fp),d0 # need to deliver signals before + # returning to userspace + and EPSW_nSL,d0 + beq resume_kernel # returning to supervisor mode + +ENTRY(resume_userspace) + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + + # is there any work to be done on int/exception return? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + bne work_pending + jmp restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + mov (TI_preempt_count,a2),d0 # non-zero preempt_count ? + cmp 0,d0 + bne restore_all + +need_resched: + btst _TIF_NEED_RESCHED,(TI_flags,a2) + beq restore_all + mov (REG_EPSW,fp),d0 + and EPSW_IM,d0 + cmp EPSW_IM_7,d0 # interrupts off (exception path) ? + beq restore_all + call preempt_schedule_irq[],0 + jmp need_resched +#endif + + +############################################################################### +# +# IRQ handler entry point +# - intended to be entered at multiple priorities +# +############################################################################### +ENTRY(irq_handler) + add -4,sp + SAVE_ALL + + # it's not a syscall + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + call do_IRQ[],0 # do_IRQ(regs) + + jmp ret_from_intr + +############################################################################### +# +# Monitor Signal handler entry point +# +############################################################################### +ENTRY(monitor_signal) + movbu (0xae000001),d1 + cmp 1,d1 + beq monsignal + ret [],0 + +monsignal: + or EPSW_NMID,epsw + mov d0,a0 + mov a0,sp + mov (REG_EPSW,fp),d1 + and ~EPSW_nSL,d1 + mov d1,(REG_EPSW,fp) + movm (sp),[d2,d3,a2,a3,exreg0,exreg1,exother] + mov (sp),a1 + mov a1,usp + movm (sp),[other] + add 4,sp +here: jmp 0x8e000008-here+0x8e000008 + +############################################################################### +# +# Double Fault handler entry point +# - note that there will not be a stack, D0/A0 will hold EPSW/PC as were +# +############################################################################### + .section .bss + .balign THREAD_SIZE + .space THREAD_SIZE +__df_stack: + .previous + +ENTRY(double_fault) + mov a0,(__df_stack-4) # PC as was + mov d0,(__df_stack-8) # EPSW as was + mn10300_set_dbfleds # display 'db-f' on the LEDs + mov 0xaa55aa55,d0 + mov d0,(__df_stack-12) # no ORIG_D0 + mov sp,a0 # save corrupted SP + mov __df_stack-12,sp # emergency supervisor stack + SAVE_ALL + mov a0,(REG_A0,fp) # save corrupted SP as A0 (which got + # clobbered by the CPU) + mov fp,d0 + calls do_double_fault +double_fault_loop: + bra double_fault_loop + +############################################################################### +# +# Bus Error handler entry point +# - handle external (async) bus errors separately +# +############################################################################### +ENTRY(raw_bus_error) + add -4,sp + mov d0,(sp) + mov (BCBERR),d0 # what + btst BCBERR_BEMR_DMA,d0 # see if it was an external bus error + beq __common_exception_aux # it wasn't + + SAVE_ALL + mov (BCBEAR),d1 # destination of erroneous access + + mov (REG_ORIG_D0,fp),d2 + mov d2,(REG_D0,fp) + mov -1,d2 + mov d2,(REG_ORIG_D0,fp) + + add -4,sp + mov fp,(12,sp) # frame pointer + call io_bus_error[],0 + jmp restore_all + +############################################################################### +# +# Miscellaneous exception entry points +# +############################################################################### +ENTRY(nmi_handler) + add -4,sp + mov d0,(sp) + mov (TBR),d0 + bra __common_exception_nonmi + +ENTRY(__common_exception) + add -4,sp + mov d0,(sp) + +__common_exception_aux: + mov (TBR),d0 + and ~EPSW_NMID,epsw # turn NMIs back on if not NMI + or EPSW_IE,epsw + +__common_exception_nonmi: + and 0x0000FFFF,d0 # turn the exception code into a vector + # table index + + btst 0x00000007,d0 + bne 1f + cmp 0x00000400,d0 + bge 1f + + SAVE_ALL # build the stack frame + + mov (REG_D0,fp),a2 # get the exception number + mov (REG_ORIG_D0,fp),d0 + mov d0,(REG_D0,fp) + mov -1,d0 + mov d0,(REG_ORIG_D0,fp) + +#ifdef CONFIG_GDBSTUB + btst 0x01,(gdbstub_busy) + beq 2f + and ~EPSW_IE,epsw + mov fp,d0 + mov a2,d1 + call gdbstub_exception[],0 # gdbstub itself caused an exception + bra restore_all +2: +#endif + + mov fp,d0 # arg 0: stacked register file + mov a2,d1 # arg 1: exception number + lsr 1,a2 + + mov (exception_table,a2),a2 + calls (a2) + jmp ret_from_exception + +1: pi # BUG() equivalent + +############################################################################### +# +# Exception handler functions table +# +############################################################################### + .data +ENTRY(exception_table) + .rept 0x400>>1 + .long uninitialised_exception + .endr + .previous + +############################################################################### +# +# Change an entry in the exception table +# - D0 exception code, D1 handler +# +############################################################################### +ENTRY(set_excp_vector) + lsr 1,d0 + add exception_table,d0 + mov d1,(d0) + mov 4,d1 +#if defined(CONFIG_MN10300_CACHE_WBACK) + jmp mn10300_dcache_flush_inv_range2 +#else + ret [],0 +#endif + +############################################################################### +# +# System call table +# +############################################################################### + .data +ENTRY(sys_call_table) + .long sys_restart_syscall /* 0 */ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 - old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old sys_olduname */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old sys_uname */ + .long sys_ni_syscall /* 110 - iopl */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* reserved for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* reserved for streams1 */ + .long sys_ni_syscall /* reserved for streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_mbind + .long sys_get_mempolicy /* 275 */ + .long sys_set_mempolicy + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_kexec_load + .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_cacheflush + .long sys_ioprio_set /* 290 */ + .long sys_ioprio_get + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages /* 295 */ + .long sys_openat + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat /* 300 */ + .long sys_fstatat64 + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat /* 305 */ + .long sys_readlinkat + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll /* 310 */ + .long sys_unshare + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range /* 315 */ + .long sys_tee + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait /* 320 */ + .long sys_utimensat + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate /* 325 */ + .long sys_timerfd_settime + .long sys_timerfd_gettime + + +nr_syscalls=(.-sys_call_table)/4 diff --git a/arch/mn10300/kernel/fpu-low.S b/arch/mn10300/kernel/fpu-low.S new file mode 100644 index 00000000000..96cfd47e68d --- /dev/null +++ b/arch/mn10300/kernel/fpu-low.S @@ -0,0 +1,197 @@ +/* MN10300 Low level FPU management operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cpu-regs.h> + +############################################################################### +# +# void fpu_init_state(void) +# - initialise the FPU +# +############################################################################### + .globl fpu_init_state + .type fpu_init_state,@function +fpu_init_state: + mov epsw,d0 + or EPSW_FE,epsw + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + fmov 0,fs0 + fmov fs0,fs1 + fmov fs0,fs2 + fmov fs0,fs3 + fmov fs0,fs4 + fmov fs0,fs5 + fmov fs0,fs6 + fmov fs0,fs7 + fmov fs0,fs8 + fmov fs0,fs9 + fmov fs0,fs10 + fmov fs0,fs11 + fmov fs0,fs12 + fmov fs0,fs13 + fmov fs0,fs14 + fmov fs0,fs15 + fmov fs0,fs16 + fmov fs0,fs17 + fmov fs0,fs18 + fmov fs0,fs19 + fmov fs0,fs20 + fmov fs0,fs21 + fmov fs0,fs22 + fmov fs0,fs23 + fmov fs0,fs24 + fmov fs0,fs25 + fmov fs0,fs26 + fmov fs0,fs27 + fmov fs0,fs28 + fmov fs0,fs29 + fmov fs0,fs30 + fmov fs0,fs31 + fmov FPCR_INIT,fpcr + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + mov d0,epsw + ret [],0 + + .size fpu_init_state,.-fpu_init_state + +############################################################################### +# +# void fpu_save(struct fpu_state_struct *) +# - save the fpu state +# - note that an FPU Operational exception might occur during this process +# +############################################################################### + .globl fpu_save + .type fpu_save,@function +fpu_save: + mov epsw,d1 + or EPSW_FE,epsw /* enable the FPU so we can access it */ + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + mov d0,a0 + fmov fs0,(a0+) + fmov fs1,(a0+) + fmov fs2,(a0+) + fmov fs3,(a0+) + fmov fs4,(a0+) + fmov fs5,(a0+) + fmov fs6,(a0+) + fmov fs7,(a0+) + fmov fs8,(a0+) + fmov fs9,(a0+) + fmov fs10,(a0+) + fmov fs11,(a0+) + fmov fs12,(a0+) + fmov fs13,(a0+) + fmov fs14,(a0+) + fmov fs15,(a0+) + fmov fs16,(a0+) + fmov fs17,(a0+) + fmov fs18,(a0+) + fmov fs19,(a0+) + fmov fs20,(a0+) + fmov fs21,(a0+) + fmov fs22,(a0+) + fmov fs23,(a0+) + fmov fs24,(a0+) + fmov fs25,(a0+) + fmov fs26,(a0+) + fmov fs27,(a0+) + fmov fs28,(a0+) + fmov fs29,(a0+) + fmov fs30,(a0+) + fmov fs31,(a0+) + fmov fpcr,d0 + mov d0,(a0) +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + + mov d1,epsw + ret [],0 + + .size fpu_save,.-fpu_save + +############################################################################### +# +# void fpu_restore(struct fpu_state_struct *) +# - restore the fpu state +# - note that an FPU Operational exception might occur during this process +# +############################################################################### + .globl fpu_restore + .type fpu_restore,@function +fpu_restore: + mov epsw,d1 + or EPSW_FE,epsw /* enable the FPU so we can access it */ + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + mov d0,a0 + fmov (a0+),fs0 + fmov (a0+),fs1 + fmov (a0+),fs2 + fmov (a0+),fs3 + fmov (a0+),fs4 + fmov (a0+),fs5 + fmov (a0+),fs6 + fmov (a0+),fs7 + fmov (a0+),fs8 + fmov (a0+),fs9 + fmov (a0+),fs10 + fmov (a0+),fs11 + fmov (a0+),fs12 + fmov (a0+),fs13 + fmov (a0+),fs14 + fmov (a0+),fs15 + fmov (a0+),fs16 + fmov (a0+),fs17 + fmov (a0+),fs18 + fmov (a0+),fs19 + fmov (a0+),fs20 + fmov (a0+),fs21 + fmov (a0+),fs22 + fmov (a0+),fs23 + fmov (a0+),fs24 + fmov (a0+),fs25 + fmov (a0+),fs26 + fmov (a0+),fs27 + fmov (a0+),fs28 + fmov (a0+),fs29 + fmov (a0+),fs30 + fmov (a0+),fs31 + mov (a0),d0 + fmov d0,fpcr +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + + mov d1,epsw + ret [],0 + + .size fpu_restore,.-fpu_restore diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c new file mode 100644 index 00000000000..e705f25ad5f --- /dev/null +++ b/arch/mn10300/kernel/fpu.c @@ -0,0 +1,223 @@ +/* MN10300 FPU management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include <asm/elf.h> +#include <asm/exceptions.h> + +struct task_struct *fpu_state_owner; + +/* + * handle an exception due to the FPU being disabled + */ +asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) +{ + struct task_struct *tsk = current; + + if (!user_mode(regs)) + die_if_no_fixup("An FPU Disabled exception happened in" + " kernel space\n", + regs, code); + +#ifdef CONFIG_FPU + preempt_disable(); + + /* transfer the last process's FPU state to memory */ + if (fpu_state_owner) { + fpu_save(&fpu_state_owner->thread.fpu_state); + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + } + + /* the current process now owns the FPU state */ + fpu_state_owner = tsk; + regs->epsw |= EPSW_FE; + + /* load the FPU with the current process's FPU state or invent a new + * clean one if the process doesn't have one */ + if (is_using_fpu(tsk)) { + fpu_restore(&tsk->thread.fpu_state); + } else { + fpu_init_state(); + set_using_fpu(tsk); + } + + preempt_enable(); +#else + { + siginfo_t info; + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *) tsk->thread.uregs->pc; + info.si_code = FPE_FLTINV; + + force_sig_info(SIGFPE, &info, tsk); + } +#endif /* CONFIG_FPU */ +} + +/* + * handle an FPU operational exception + * - there's a possibility that if the FPU is asynchronous, the signal might + * be meant for a process other than the current one + */ +asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) +{ + struct task_struct *tsk = fpu_state_owner; + siginfo_t info; + + if (!user_mode(regs)) + die_if_no_fixup("An FPU Operation exception happened in" + " kernel space\n", + regs, code); + + if (!tsk) + die_if_no_fixup("An FPU Operation exception happened," + " but the FPU is not in use", + regs, code); + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *) tsk->thread.uregs->pc; + info.si_code = FPE_FLTINV; + +#ifdef CONFIG_FPU + { + u32 fpcr; + + /* get FPCR (we need to enable the FPU whilst we do this) */ + asm volatile(" or %1,epsw \n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + " nop \n" + " nop \n" + " nop \n" +#endif + " fmov fpcr,%0 \n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + " nop \n" + " nop \n" + " nop \n" +#endif + " and %2,epsw \n" + : "=&d"(fpcr) + : "i"(EPSW_FE), "i"(~EPSW_FE) + ); + + if (fpcr & FPCR_EC_Z) + info.si_code = FPE_FLTDIV; + else if (fpcr & FPCR_EC_O) + info.si_code = FPE_FLTOVF; + else if (fpcr & FPCR_EC_U) + info.si_code = FPE_FLTUND; + else if (fpcr & FPCR_EC_I) + info.si_code = FPE_FLTRES; + } +#endif + + force_sig_info(SIGFPE, &info, tsk); +} + +/* + * save the FPU state to a signal context + */ +int fpu_setup_sigcontext(struct fpucontext *fpucontext) +{ +#ifdef CONFIG_FPU + struct task_struct *tsk = current; + + if (!is_using_fpu(tsk)) + return 0; + + /* transfer the current FPU state to memory and cause fpu_init() to be + * triggered by the next attempted FPU operation by the current + * process. + */ + preempt_disable(); + + if (fpu_state_owner == tsk) { + fpu_save(&tsk->thread.fpu_state); + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } + + preempt_enable(); + + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); + + /* transfer the saved FPU state onto the userspace stack */ + if (copy_to_user(fpucontext, + &tsk->thread.fpu_state, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext)))) + return -1; + + return 1; +#else + return 0; +#endif +} + +/* + * kill a process's FPU state during restoration after signal handling + */ +void fpu_kill_state(struct task_struct *tsk) +{ +#ifdef CONFIG_FPU + /* disown anything left in the FPU */ + preempt_disable(); + + if (fpu_state_owner == tsk) { + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } + + preempt_enable(); +#endif + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); +} + +/* + * restore the FPU state from a signal context + */ +int fpu_restore_sigcontext(struct fpucontext *fpucontext) +{ + struct task_struct *tsk = current; + int ret; + + /* load up the old FPU state */ + ret = copy_from_user(&tsk->thread.fpu_state, + fpucontext, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext))); + if (!ret) + set_using_fpu(tsk); + + return ret; +} + +/* + * fill in the FPU structure for a core dump + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) +{ + struct task_struct *tsk = current; + int fpvalid; + + fpvalid = is_using_fpu(tsk); + if (fpvalid) { + unlazy_fpu(tsk); + memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg)); + } + + return fpvalid; +} diff --git a/arch/mn10300/kernel/gdb-cache.S b/arch/mn10300/kernel/gdb-cache.S new file mode 100644 index 00000000000..1108badc3d3 --- /dev/null +++ b/arch/mn10300/kernel/gdb-cache.S @@ -0,0 +1,105 @@ +############################################################################### +# +# MN10300 Low-level cache purging routines for gdbstub +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cache.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> +#include <asm/frame.inc> +#include <asm/serial-regs.h> + + .text + +############################################################################### +# +# GDB stub cache purge +# +############################################################################### + .type gdbstub_purge_cache,@function +ENTRY(gdbstub_purge_cache) + ####################################################################### + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + +mn10300_dcache_flush_skip: + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_loop + +;; # unconditionally flush and invalidate the dcache +;; mov DCACHE_PURGE(0,0),a1 # dcache purge request address +;; mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of +;; # entries +;; +;; gdbstub_purge_cache__dcache_loop: +;; mov (a1),d0 # unconditional purge +;; +;; add L1_CACHE_BYTES,a1 +;; add -1,d1 +;; bne gdbstub_purge_cache__dcache_loop + + ####################################################################### + # now invalidate the icache + mov CHCTR,a0 + movhu (a0),a1 + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the icache + and ~CHCTR_ICEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + movhu a1,(a0) + movhu (a0),d0 # read back to flush + # (SIGILLs all over without this) + + mov d1,epsw + + ret [],0 + + .size gdbstub_purge_cache,.-gdbstub_purge_cache diff --git a/arch/mn10300/kernel/gdb-io-serial-low.S b/arch/mn10300/kernel/gdb-io-serial-low.S new file mode 100644 index 00000000000..c68dcd05220 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial-low.S @@ -0,0 +1,90 @@ +############################################################################### +# +# 16550 serial Rx interrupt handler for gdbstub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/thread_info.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <asm/unit/serial.h> + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + +#if 1 + movbu (GDBPORT_SERIAL_IIR),d2 +#endif + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and 0x00000fff,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (GDBPORT_SERIAL_LSR),d3 + btst UART_LSR_DR,d3 + beq gdbstub_io_rx_done + movbu (GDBPORT_SERIAL_RX),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(XIRQxICR(GDBPORT_SERIAL_IRQ)) # ACK the interrupt + movhu (XIRQxICR(GDBPORT_SERIAL_IRQ)),d2 # flush + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +gdbstub_io_rx_enter: + or EPSW_IE|EPSW_IM_1,epsw + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_rx_irq(regs,excep) + + and ~EPSW_IE,epsw + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c new file mode 100644 index 00000000000..9a6d4e8ebe7 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial.c @@ -0,0 +1,155 @@ +/* 16550 serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/nmi.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/serial-regs.h> +#include <asm/unit/serial.h> + +/* + * initialise the GDB stub + */ +void gdbstub_io_init(void) +{ + u16 tmp; + + /* set up the serial port */ + GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */ + GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + + FLOWCTL_CLEAR(DTR); + FLOWCTL_SET(RTS); + + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + XIRQxICR(GDBPORT_SERIAL_IRQ) = 0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + IVAR0 = EXCEP_IRQ_LEVEL0; + set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler); + + XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST; + XIRQxICR(GDBPORT_SERIAL_IRQ) = GxICR_ENABLE | GxICR_LEVEL_0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI; + + /* permit level 0 IRQs to take place */ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE | EPSW_IM_1) + ); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + unsigned value; + u8 lcr; + + value = 18432000 / 16 / baud; + + lcr = GDBPORT_SERIAL_LCR; + GDBPORT_SERIAL_LCR |= UART_LCR_DLAB; + GDBPORT_SERIAL_DLL = value & 0xff; + GDBPORT_SERIAL_DLM = (value >> 8) & 0xff; + GDBPORT_SERIAL_LCR = lcr; +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + + try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + watchdog_alert_counter = 0; +#endif /* CONFIG_MN10300_WD_TIMER */ + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + gdbstub_rx_outp = ix & 0x00000fff; + + if (st & UART_LSR_BI) { + gdbstub_proto("### GDB Rx Break Detected ###\n"); + return -EINTR; + } else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) { + gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st); + return -EIO; + } else { + gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + FLOWCTL_SET(DTR); + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + } + GDBPORT_SERIAL_TX = ch; + + FLOWCTL_CLEAR(DTR); +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_flush(void) +{ + LSR_WAIT_FOR(TEMT); + LSR_WAIT_FOR(THRE); + FLOWCTL_CLEAR(DTR); +} diff --git a/arch/mn10300/kernel/gdb-io-ttysm-low.S b/arch/mn10300/kernel/gdb-io-ttysm-low.S new file mode 100644 index 00000000000..677c7876307 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm-low.S @@ -0,0 +1,93 @@ +############################################################################### +# +# MN10300 On-chip serial Rx interrupt handler for GDB stub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <asm/unit/serial.h> +#include "mn10300-serial.h" + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and PAGE_SIZE_asm-1,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (SCgSTR),d3 + btst SC01STR_RBF,d3 + beq gdbstub_io_rx_done + movbu (SCgRXB),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(GxICR(SCgRXIRQ)) # ACK the interrupt + movhu (GxICR(SCgRXIRQ)),d2 # flush + + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +############################################################################### +# +# debugging interrupt - enter the GDB stub proper +# +############################################################################### +gdbstub_io_rx_enter: + or EPSW_IE|EPSW_IM_1,epsw + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_io_rx_irq(regs,excep) + + and ~EPSW_IE,epsw + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c new file mode 100644 index 00000000000..c5451592d40 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm.c @@ -0,0 +1,299 @@ +/* MN10300 On-chip serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/unit/clock.h> +#include "mn10300-serial.h" + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif0; +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif1; +#else +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif2; +#endif + + +/* + * initialise the GDB stub I/O routines + */ +void __init gdbstub_io_init(void) +{ + uint16_t scxctr; + int tmp; + + switch (gdbstub_port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + gdbstub_port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + gdbstub_port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + /* set up the serial port */ + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + set_intr_level(gdbstub_port->rx_irq, GxICR_LEVEL_0); + set_intr_level(gdbstub_port->tx_irq, GxICR_LEVEL_0); + set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler); + + *gdbstub_port->rx_icr |= GxICR_ENABLE; + tmp = *gdbstub_port->rx_icr; + + /* enable the device */ + scxctr = SC01CTR_CLN_8BIT; /* 1N8 */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + scxctr |= SC0CTR_CK_TM8UFLOW_8; /* == SC1CTR_CK_TM9UFLOW_8 + == SC2CTR_CK_TM10UFLOW_8 */ + break; + + case MNSCx_DIV_TIMER_8BIT: + scxctr |= SC0CTR_CK_TM2UFLOW_8; + break; + } + + scxctr |= SC01CTR_TXE | SC01CTR_RXE; + + *gdbstub_port->_control = scxctr; + tmp = *gdbstub_port->_control; + + /* permit level 0 IRQs only */ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE|EPSW_IM_1) + ); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + const unsigned bits = 10; /* 1 [start] + 8 [data] + 0 [parity] + + * 1 [stop] */ + unsigned long ioclk = gdbstub_port->ioclk; + unsigned xdiv, tmp; + uint16_t tmxbr; + uint8_t tmxmd; + + if (!baud) { + baud = 9600; + } else if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + +try_alternative: + xdiv = 1; + + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + break; + + case MNSCx_DIV_TIMER_8BIT: + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + } + + /* as a last resort, if the quotient is zero, default to 9600 bps */ + baud = 9600; + goto try_alternative; + +timer_okay: + gdbstub_port->uart.timeout = (2 * bits * HZ) / baud; + gdbstub_port->uart.timeout += HZ / 50; + + /* set the timer to produce the required baud rate */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *gdbstub_port->_tmxmd = 0; + *gdbstub_port->_tmxbr = tmxbr; + *gdbstub_port->_tmxmd = TM8MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *gdbstub_port->_tmxmd = 0; + *(volatile u8 *) gdbstub_port->_tmxbr = (u8)tmxbr; + *gdbstub_port->_tmxmd = TM2MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + +try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + watchdog_alert_counter = 0; +#endif /* CONFIG_MN10300_WD_TIMER */ + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + gdbstub_rx_outp = ix & (PAGE_SIZE - 1); + + st &= SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF | + SC01STR_OEF; + + /* deal with what we've got + * - note that the UART doesn't do BREAK-detection for us + */ + if (st & SC01STR_FEF && ch == 0) { + switch (gdbstub_port->rx_brk) { + case 0: gdbstub_port->rx_brk = 1; goto try_again; + case 1: gdbstub_port->rx_brk = 2; goto try_again; + case 2: + gdbstub_port->rx_brk = 3; + gdbstub_proto("### GDB MNSERIAL Rx Break Detected" + " ###\n"); + return -EINTR; + default: + goto try_again; + } + } else if (st & SC01STR_FEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Framing Error ###\n"); + return -EIO; + } else if (st & SC01STR_OEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Overrun Error ###\n"); + return -EIO; + } else if (st & SC01STR_PEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Parity Error ###\n"); + return -EIO; + } else { + /* look for the tail-end char on a break run */ + if (gdbstub_port->rx_brk == 3) { + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + gdbstub_port->rx_brk = 0; + goto try_again; + default: + break; + } + } + + gdbstub_port->rx_brk = 0; + gdbstub_io("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + *(u8 *) gdbstub_port->_txb = 0x0d; + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + } + + *(u8 *) gdbstub_port->_txb = ch; +} + +/* + * flush the transmission buffers + */ +void gdbstub_io_tx_flush(void) +{ + while (*gdbstub_port->_status & (SC01STR_TBF | SC01STR_TXF)) + continue; +} diff --git a/arch/mn10300/kernel/gdb-low.S b/arch/mn10300/kernel/gdb-low.S new file mode 100644 index 00000000000..e2725552cd8 --- /dev/null +++ b/arch/mn10300/kernel/gdb-low.S @@ -0,0 +1,115 @@ +############################################################################### +# +# MN10300 Low-level gdbstub routines +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cache.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> +#include <asm/frame.inc> +#include <asm/serial-regs.h> + + .text + +############################################################################### +# +# GDB stub read memory with guard +# - D0 holds the memory address to read +# - D1 holds the address to store the byte into +# +############################################################################### + .globl gdbstub_read_byte_guard + .globl gdbstub_read_byte_cont +ENTRY(gdbstub_read_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_byte_guard: + movbu (a0),d1 +gdbstub_read_byte_cont: + movbu d1,(a1) + ret [],0 + + .globl gdbstub_read_word_guard + .globl gdbstub_read_word_cont +ENTRY(gdbstub_read_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_word_guard: + movhu (a0),d1 +gdbstub_read_word_cont: + movhu d1,(a1) + ret [],0 + + .globl gdbstub_read_dword_guard + .globl gdbstub_read_dword_cont +ENTRY(gdbstub_read_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_dword_guard: + mov (a0),d1 +gdbstub_read_dword_cont: + mov d1,(a1) + ret [],0 + +############################################################################### +# +# GDB stub write memory with guard +# - D0 holds the byte to store +# - D1 holds the memory address to write +# +############################################################################### + .globl gdbstub_write_byte_guard + .globl gdbstub_write_byte_cont +ENTRY(gdbstub_write_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_byte_guard: + movbu a0,(a1) +gdbstub_write_byte_cont: + ret [],0 + + .globl gdbstub_write_word_guard + .globl gdbstub_write_word_cont +ENTRY(gdbstub_write_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_word_guard: + movhu a0,(a1) +gdbstub_write_word_cont: + ret [],0 + + .globl gdbstub_write_dword_guard + .globl gdbstub_write_dword_cont +ENTRY(gdbstub_write_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_dword_guard: + mov a0,(a1) +gdbstub_write_dword_cont: + ret [],0 + +############################################################################### +# +# GDB stub BUG() trap +# +############################################################################### +ENTRY(__gdbstub_bug_trap) + .byte 0xF7,0xF7 # don't use 0xFF as the JTAG unit preempts that + ret [],0 diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c new file mode 100644 index 00000000000..21891c71d54 --- /dev/null +++ b/arch/mn10300/kernel/gdb-stub.c @@ -0,0 +1,1947 @@ +/* MN10300 GDB stub + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified for Linux/mn10300 by David Howells <dhowells@redhat.com> + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + * + * ============== + * MORE EXAMPLES: + * ============== + * + * For reference -- the following are the steps that one + * company took (RidgeRun Inc) to get remote gdb debugging + * going. In this scenario the host machine was a PC and the + * target platform was a Galileo EVB64120A MIPS evaluation + * board. + * + * Step 1: + * First download gdb-5.0.tar.gz from the internet. + * and then build/install the package. + * + * Example: + * $ tar zxf gdb-5.0.tar.gz + * $ cd gdb-5.0 + * $ ./configure --target=am33_2.0-linux-gnu + * $ make + * $ install + * am33_2.0-linux-gnu-gdb + * + * Step 2: + * Configure linux for remote debugging and build it. + * + * Example: + * $ cd ~/linux + * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> + * $ make dep; make vmlinux + * + * Step 3: + * Download the kernel to the remote target and start + * the kernel running. It will promptly halt and wait + * for the host gdb session to connect. It does this + * since the "Kernel Hacking" option has defined + * CONFIG_REMOTE_DEBUG which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ am33_2.0-linux-gnu-gdb vmlinux + * (gdb) set remotebaud 115200 + * (gdb) target remote /dev/ttyS1 + * ...at this point you are connected to + * the remote target and can use gdb + * in the normal fasion. Setting + * breakpoints, single stepping, + * printing variables, etc. + * + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/bug.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/cacheflush.h> +#include <asm/serial-regs.h> +#include <asm/busctl-regs.h> +#include <asm/unit/leds.h> +#include <asm/unit/serial.h> + +/* define to use F7F7 rather than FF which is subverted by JTAG debugger */ +#undef GDBSTUB_USE_F7F7_AS_BREAKPOINT + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static const char gdbstub_banner[] = + "Linux/MN10300 GDB Stub (c) RedHat 2007\n"; + +u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +u32 gdbstub_rx_inp; +u32 gdbstub_rx_outp; +u8 gdbstub_busy; +u8 gdbstub_rx_overflow; +u8 gdbstub_rx_unget; + +static u8 gdbstub_flush_caches; +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static char trans_buffer[BUFMAX]; + +static const char hexchars[] = "0123456789abcdef"; + +struct gdbstub_bkpt { + u8 *addr; /* address of breakpoint */ + u8 len; /* size of breakpoint */ + u8 origbytes[7]; /* original bytes */ +}; + +static struct gdbstub_bkpt gdbstub_bkpts[256]; + +/* + * local prototypes + */ +static void getpacket(char *buffer); +static int putpacket(char *buffer); +static int computeSignal(enum exception_code excep); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(const void *mem, char *buf, int count, + int may_fault); +static const char *hex2mem(const char *buf, void *_mem, int count, + int may_fault); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + return -1; +} + +#ifdef CONFIG_GDBSTUB_DEBUGGING + +void debug_to_serial(const char *p, int n) +{ + __debug_to_serial(p, n); + /* gdbstub_console_write(NULL, p, n); */ +} + +void gdbstub_printk(const char *fmt, ...) +{ + va_list args; + int len; + + /* Emit the output into the temporary buffer */ + va_start(args, fmt); + len = vsnprintf(trans_buffer, sizeof(trans_buffer), fmt, args); + va_end(args); + debug_to_serial(trans_buffer, len); +} + +#endif + +static inline char *gdbstub_strcpy(char *dst, const char *src) +{ + int loop = 0; + while ((dst[loop] = src[loop])) + loop++; + return dst; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + unsigned char ch; + int count, i, ret, error; + + for (;;) { + /* + * wait around for the start character, + * ignore all other characters + */ + do { + gdbstub_io_rx_char(&ch, 0); + } while (ch != '$'); + + checksum = 0; + xmitcsum = -1; + count = 0; + error = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + + if (ch == '#') + break; + checksum += ch; + buffer[count] = ch; + count++; + } + + if (error == -EIO) { + gdbstub_proto("### GDB Rx Error - Skipping packet" + " ###\n"); + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + if (count >= BUFMAX || error) + continue; + + buffer[count] = 0; + + /* read the checksum */ + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum = hex(ch) << 4; + + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum |= hex(ch); + + if (error) { + if (error == -EIO) + gdbstub_io("### GDB Rx Error -" + " Skipping packet\n"); + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + /* check the checksum */ + if (checksum != xmitcsum) { + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* failed checksum */ + continue; + } + + gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum); + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + gdbstub_io_tx_char(buffer[0]); + gdbstub_io_tx_char(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = 0; + while (buffer[count]) + count++; + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + + break; + } +} + +/* + * send the packet in buffer. + * - return 0 if successfully ACK'd + * - return 1 if abandoned due to new incoming packet + */ +static int putpacket(char *buffer) +{ + unsigned char checksum; + unsigned char ch; + int count; + + /* + * $<packet info>#<checksum>. + */ + gdbstub_proto("### GDB Tx $'%s'#?? ###\n", buffer); + + do { + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hexchars[checksum >> 4]); + gdbstub_io_tx_char(hexchars[checksum & 0xf]); + + } while (gdbstub_io_rx_char(&ch, 0), + ch == '-' && (gdbstub_io("### GDB Rx NAK\n"), 0), + ch != '-' && ch != '+' && + (gdbstub_io("### GDB Rx ??? %02x\n", ch), 0), + ch != '+' && ch != '$'); + + if (ch == '+') { + gdbstub_io("### GDB Rx ACK\n"); + return 0; + } + + gdbstub_io("### GDB Tx Abandoned\n"); + gdbstub_rx_unget = ch; + return 1; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars++; + + (*ptr)++; + } + + return (numChars); +} + +/* + * We single-step by setting breakpoints. When an exception + * is handled, we need to restore the instructions hoisted + * when the breakpoints were set. + * + * This is where we save the original instructions. + */ +static struct gdb_bp_save { + u8 *addr; + u8 opcode[2]; +} step_bp[2]; + +static const unsigned char gdbstub_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +static int __gdbstub_mark_bp(u8 *addr, int ix) +{ + if (addr < (u8 *) 0x70000000UL) + return 0; + /* 70000000-7fffffff: vmalloc area */ + if (addr < (u8 *) 0x80000000UL) + goto okay; + if (addr < (u8 *) 0x8c000000UL) + return 0; + /* 8c000000-93ffffff: SRAM, SDRAM */ + if (addr < (u8 *) 0x94000000UL) + goto okay; + return 0; + +okay: + if (gdbstub_read_byte(addr + 0, &step_bp[ix].opcode[0]) < 0 || + gdbstub_read_byte(addr + 1, &step_bp[ix].opcode[1]) < 0) + return 0; + + step_bp[ix].addr = addr; + return 1; +} + +static inline void __gdbstub_restore_bp(void) +{ +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (step_bp[0].addr) { + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + gdbstub_write_byte(step_bp[0].opcode[1], step_bp[0].addr + 1); + } + if (step_bp[1].addr) { + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); + gdbstub_write_byte(step_bp[1].opcode[1], step_bp[1].addr + 1); + } +#else + if (step_bp[0].addr) + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + if (step_bp[1].addr) + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); +#endif + + gdbstub_flush_caches = 1; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; +} + +/* + * emulate single stepping by means of breakpoint instructions + */ +static int gdbstub_single_step(struct pt_regs *regs) +{ + unsigned size; + uint32_t x; + uint8_t cur, *pc, *sp; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; + x = 0; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + if (gdbstub_read_byte(pc, &cur) < 0) + return -EFAULT; + + gdbstub_bkpt("Single Step from %p { %02x }\n", pc, cur); + + gdbstub_flush_caches = 1; + + size = gdbstub_insn_sizes[cur]; + if (size > 0) { + if (!__gdbstub_mark_bp(pc + size, 0)) + goto fault; + } else { + switch (cur) { + /* Bxx (d8,PC) */ + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc8: + case 0xc9: + case 0xca: + if (gdbstub_read_byte(pc + 1, (u8 *) &x) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + if ((x < 0 || x > 2) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + break; + + /* LXX (d8,PC) */ + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + case 0xd4: + case 0xd5: + case 0xd6: + case 0xd7: + case 0xd8: + case 0xd9: + case 0xda: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + if (regs->pc != regs->lar && + !__gdbstub_mark_bp((u8 *) regs->lar, 1)) + goto fault; + break; + + /* SETLB - loads the next for bytes into the LIR + * register */ + case 0xdb: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(pc + 3, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(pc + 4, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s32) x, 0)) + goto fault; + break; + + /* RETF */ + case 0xde: + if (!__gdbstub_mark_bp((u8 *) regs->mdr, 0)) + goto fault; + break; + + /* RET */ + case 0xdf: + if (gdbstub_read_byte(pc + 2, (u8 *) &x) < 0) + goto fault; + sp += (s8)x; + if (gdbstub_read_byte(sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + break; + + case 0xf0: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur >= 0xf0 && cur <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (cur & 3) { + case 0: x = regs->a0; break; + case 1: x = regs->a1; break; + case 2: x = regs->a2; break; + case 3: x = regs->a3; break; + } + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfc) { + /* RETS */ + if (gdbstub_read_byte( + sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfd) { + /* RTI */ + if (gdbstub_read_byte( + sp + 4, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 5, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 6, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 7, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + } + + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 3, 0)) + goto fault; + + if (cur >= 0xe8 && cur <= 0xeb) { + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0) + goto fault; + if ((x < 0 || x > 3) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + } + break; + + case 0xfa: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur == 0xff) { + /* CALLS (d16,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 4, 0)) + goto fault; + } + break; + + case 0xfc: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (cur == 0xff) { + /* CALLS (d32,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + pc + 4, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + pc + 5, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp( + pc + (s32) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp( + pc + 6, 0)) + goto fault; + } + break; + + } + } + + gdbstub_bkpt("Step: %02x at %p; %02x at %p\n", + step_bp[0].opcode[0], step_bp[0].addr, + step_bp[1].opcode[0], step_bp[1].addr); + + if (step_bp[0].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[0].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[0].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[0].addr + 0) < 0) + goto fault; +#endif + } + + if (step_bp[1].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[1].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[1].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[1].addr + 0) < 0) + goto fault; +#endif + } + + return 0; + + fault: + /* uh-oh - silly address alert, try and restore things */ + __gdbstub_restore_bp(); + return -EFAULT; +} + +#ifdef CONFIG_GDBSTUB_CONSOLE + +void gdbstub_console_write(struct console *con, const char *p, unsigned n) +{ + static const char gdbstub_cr[] = { 0x0d }; + char outbuf[26]; + int qty; + u8 busy; + + busy = gdbstub_busy; + gdbstub_busy = 1; + + outbuf[0] = 'O'; + + while (n > 0) { + qty = 1; + + while (n > 0 && qty < 20) { + mem2hex(p, outbuf + qty, 2, 0); + qty += 2; + if (*p == 0x0a) { + mem2hex(gdbstub_cr, outbuf + qty, 2, 0); + qty += 2; + } + p++; + n--; + } + + outbuf[qty] = 0; + putpacket(outbuf); + } + + gdbstub_busy = busy; +} + +static kdev_t gdbstub_console_dev(struct console *con) +{ + return MKDEV(1, 3); /* /dev/null */ +} + +static struct console gdbstub_console = { + .name = "gdb", + .write = gdbstub_console_write, + .device = gdbstub_console_dev, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +#endif + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * - if successful, return a pointer to the last char put in buf (NUL) + * - in case of mem fault, return NULL + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) +{ + const u8 *mem = _mem; + u8 ch[4]; + + if ((u32) mem & 1 && count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + mem += 2; + count -= 2; + } + + while (count >= 4) { + if (gdbstub_read_dword(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + *buf++ = hexchars[ch[2] >> 4]; + *buf++ = hexchars[ch[2] & 0xf]; + *buf++ = hexchars[ch[3] >> 4]; + *buf++ = hexchars[ch[3] & 0xf]; + mem += 4; + count -= 4; + } + + if (count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + mem += 2; + count -= 2; + } + + if (count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + } + + *buf = 0; + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +const char *hex2mem(const char *buf, void *_mem, int count, int may_fault) +{ + u8 *mem = _mem; + union { + u32 val; + u8 b[4]; + } ch; + + if ((u32) mem & 1 && count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + while (count >= 4) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + ch.b[2] = hex(*buf++) << 4; + ch.b[2] |= hex(*buf++); + ch.b[3] = hex(*buf++) << 4; + ch.b[3] |= hex(*buf++); + if (gdbstub_write_dword(ch.val, mem) != 0) + return 0; + mem += 4; + count -= 4; + } + + if (count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + if (count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + } + + return buf; +} + +/* + * This table contains the mapping between MN10300 exception codes, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static const struct excep_to_sig_map { + enum exception_code excep; /* MN10300 exception code */ + unsigned char signo; /* Signal that we map this into */ +} excep_to_sig_map[] = { + { EXCEP_ITLBMISS, SIGSEGV }, + { EXCEP_DTLBMISS, SIGSEGV }, + { EXCEP_TRAP, SIGTRAP }, + { EXCEP_ISTEP, SIGTRAP }, + { EXCEP_IBREAK, SIGTRAP }, + { EXCEP_OBREAK, SIGTRAP }, + { EXCEP_UNIMPINS, SIGILL }, + { EXCEP_UNIMPEXINS, SIGILL }, + { EXCEP_MEMERR, SIGSEGV }, + { EXCEP_MISALIGN, SIGSEGV }, + { EXCEP_BUSERROR, SIGBUS }, + { EXCEP_ILLINSACC, SIGSEGV }, + { EXCEP_ILLDATACC, SIGSEGV }, + { EXCEP_IOINSACC, SIGSEGV }, + { EXCEP_PRIVINSACC, SIGSEGV }, + { EXCEP_PRIVDATACC, SIGSEGV }, + { EXCEP_FPU_DISABLED, SIGFPE }, + { EXCEP_FPU_UNIMPINS, SIGFPE }, + { EXCEP_FPU_OPERATION, SIGFPE }, + { EXCEP_WDT, SIGALRM }, + { EXCEP_NMI, SIGQUIT }, + { EXCEP_IRQ_LEVEL0, SIGINT }, + { EXCEP_IRQ_LEVEL1, SIGINT }, + { EXCEP_IRQ_LEVEL2, SIGINT }, + { EXCEP_IRQ_LEVEL3, SIGINT }, + { EXCEP_IRQ_LEVEL4, SIGINT }, + { EXCEP_IRQ_LEVEL5, SIGINT }, + { EXCEP_IRQ_LEVEL6, SIGINT }, + { 0, 0} +}; + +/* + * convert the MN10300 exception code into a UNIX signal number + */ +static int computeSignal(enum exception_code excep) +{ + const struct excep_to_sig_map *map; + + for (map = excep_to_sig_map; map->signo; map++) + if (map->excep == excep) + return map->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +static u32 gdbstub_fpcr, gdbstub_fpufs_array[32]; + +/* + * + */ +static void gdbstub_store_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %2,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %1, a1\n" + "fmov fs0, (a1+)\n" + "fmov fs1, (a1+)\n" + "fmov fs2, (a1+)\n" + "fmov fs3, (a1+)\n" + "fmov fs4, (a1+)\n" + "fmov fs5, (a1+)\n" + "fmov fs6, (a1+)\n" + "fmov fs7, (a1+)\n" + "fmov fs8, (a1+)\n" + "fmov fs9, (a1+)\n" + "fmov fs10, (a1+)\n" + "fmov fs11, (a1+)\n" + "fmov fs12, (a1+)\n" + "fmov fs13, (a1+)\n" + "fmov fs14, (a1+)\n" + "fmov fs15, (a1+)\n" + "fmov fs16, (a1+)\n" + "fmov fs17, (a1+)\n" + "fmov fs18, (a1+)\n" + "fmov fs19, (a1+)\n" + "fmov fs20, (a1+)\n" + "fmov fs21, (a1+)\n" + "fmov fs22, (a1+)\n" + "fmov fs23, (a1+)\n" + "fmov fs24, (a1+)\n" + "fmov fs25, (a1+)\n" + "fmov fs26, (a1+)\n" + "fmov fs27, (a1+)\n" + "fmov fs28, (a1+)\n" + "fmov fs29, (a1+)\n" + "fmov fs30, (a1+)\n" + "fmov fs31, (a1+)\n" + "fmov fpcr, %0\n" + : "=d"(gdbstub_fpcr) + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE) + : "a1" + ); +#endif +} + +/* + * + */ +static void gdbstub_load_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %1,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %0, a1\n" + "fmov (a1+), fs0\n" + "fmov (a1+), fs1\n" + "fmov (a1+), fs2\n" + "fmov (a1+), fs3\n" + "fmov (a1+), fs4\n" + "fmov (a1+), fs5\n" + "fmov (a1+), fs6\n" + "fmov (a1+), fs7\n" + "fmov (a1+), fs8\n" + "fmov (a1+), fs9\n" + "fmov (a1+), fs10\n" + "fmov (a1+), fs11\n" + "fmov (a1+), fs12\n" + "fmov (a1+), fs13\n" + "fmov (a1+), fs14\n" + "fmov (a1+), fs15\n" + "fmov (a1+), fs16\n" + "fmov (a1+), fs17\n" + "fmov (a1+), fs18\n" + "fmov (a1+), fs19\n" + "fmov (a1+), fs20\n" + "fmov (a1+), fs21\n" + "fmov (a1+), fs22\n" + "fmov (a1+), fs23\n" + "fmov (a1+), fs24\n" + "fmov (a1+), fs25\n" + "fmov (a1+), fs26\n" + "fmov (a1+), fs27\n" + "fmov (a1+), fs28\n" + "fmov (a1+), fs29\n" + "fmov (a1+), fs30\n" + "fmov (a1+), fs31\n" + "fmov %2, fpcr\n" + : + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE), "d"(gdbstub_fpcr) + : "a1" + ); +#endif +} + +/* + * set a software breakpoint + */ +int gdbstub_set_breakpoint(u8 *addr, int len) +{ + int bkpt, loop, xloop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("setbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (!gdbstub_bkpts[bkpt].addr) + break; + if (bkpt < 0) + return -ENOSPC; + + for (loop = 0; loop < len; loop++) + if (gdbstub_read_byte(&addr[loop], + &gdbstub_bkpts[bkpt].origbytes[loop] + ) < 0) + return -EFAULT; + + gdbstub_flush_caches = 1; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xF7, &addr[loop]) < 0) + goto restore; +#else + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xFF, &addr[loop]) < 0) + goto restore; +#endif + + gdbstub_bkpts[bkpt].addr = addr; + gdbstub_bkpts[bkpt].len = len; + + gdbstub_bkpt("Set BKPT[%02x]: %p-%p {%02x%02x%02x%02x%02x%02x%02x}\n", + bkpt, + gdbstub_bkpts[bkpt].addr, + gdbstub_bkpts[bkpt].addr + gdbstub_bkpts[bkpt].len - 1, + gdbstub_bkpts[bkpt].origbytes[0], + gdbstub_bkpts[bkpt].origbytes[1], + gdbstub_bkpts[bkpt].origbytes[2], + gdbstub_bkpts[bkpt].origbytes[3], + gdbstub_bkpts[bkpt].origbytes[4], + gdbstub_bkpts[bkpt].origbytes[5], + gdbstub_bkpts[bkpt].origbytes[6] + ); + + return 0; + +restore: + for (xloop = 0; xloop < loop; xloop++) + gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[xloop], + addr + xloop); + return -EFAULT; +} + +/* + * clear a software breakpoint + */ +int gdbstub_clear_breakpoint(u8 *addr, int len) +{ + int bkpt, loop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("clearbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (gdbstub_bkpts[bkpt].addr == addr && + gdbstub_bkpts[bkpt].len == len) + break; + if (bkpt < 0) + return -ENOENT; + + gdbstub_bkpts[bkpt].addr = NULL; + + gdbstub_flush_caches = 1; + + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[loop], + addr + loop) < 0) + return -EFAULT; + + return 0; +} + +/* + * This function does all command processing for interfacing to gdb + * - returns 1 if the exception should be skipped, 0 otherwise. + */ +static int gdbstub(struct pt_regs *regs, enum exception_code excep) +{ + unsigned long *stack; + unsigned long epsw, mdr; + uint32_t zero, ssp; + uint8_t broke; + char *ptr; + int sigval; + int addr; + int length; + int loop; + + if (excep == EXCEP_FPU_DISABLED) + return 0; + + gdbstub_flush_caches = 0; + + mn10300_set_gdbleds(1); + + asm volatile("mov mdr,%0" : "=d"(mdr)); + asm volatile("mov epsw,%0" : "=d"(epsw)); + asm volatile("mov %0,epsw" + :: "d"((epsw & ~EPSW_IM) | EPSW_IE | EPSW_IM_1)); + + gdbstub_store_fpu(); + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + /* skip the initial pause loop */ + if (regs->pc == (unsigned long) __gdbstub_pause) + regs->pc = (unsigned long) start_kernel; +#endif + + /* if we were single stepping, restore the opcodes hoisted for the + * breakpoint[s] */ + broke = 0; + if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) || + (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc)) + broke = 1; + + __gdbstub_restore_bp(); + + if (gdbstub_rx_unget) { + sigval = SIGINT; + if (gdbstub_rx_unget != 3) + goto packet_waiting; + gdbstub_rx_unget = 0; + } + + stack = (unsigned long *) regs->sp; + sigval = broke ? SIGTRAP : computeSignal(excep); + + /* send information about a BUG() */ + if (!user_mode(regs) && excep == EXCEP_SYSCALL15) { + const struct bug_entry *bug; + + bug = find_bug(regs->pc); + if (bug) + goto found_bug; + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx\n", regs->pc); + goto send_bug_pkt; + + found_bug: + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx (%s:%d)\n", + regs->pc, bug->file, bug->line); + + send_bug_pkt: + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(trans_buffer, ptr, length, 0); + *ptr = 0; + putpacket(output_buffer); + + regs->pc -= 2; + sigval = SIGABRT; + } else if (regs->pc == (unsigned long) __gdbstub_bug_trap) { + regs->pc = regs->mdr; + sigval = SIGABRT; + } + + /* + * send a message to the debugger's user saying what happened if it may + * not be clear cut (we can't map exceptions onto signals properly) + */ + if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) { + static const char title[] = "Excep ", tbcberr[] = "BCBERR "; + static const char crlf[] = "\r\n"; + char hx; + u32 bcberr = BCBERR; + + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(title, ptr, sizeof(title) - 1, 0); + + hx = hexchars[(excep & 0xf000) >> 12]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x0f00) >> 8]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x00f0) >> 4]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x000f)]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + + /* BCBERR */ + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0); + + hx = hexchars[(bcberr & 0xf0000000) >> 28]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0f000000) >> 24]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x00f00000) >> 20]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x000f0000) >> 16]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0000f000) >> 12]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x00000f00) >> 8]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x000000f0) >> 4]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0000000f)]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + } + + /* + * tell the debugger that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[GDB_REGID_PC >> 4]; + *ptr++ = hexchars[GDB_REGID_PC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(®s->pc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[GDB_REGID_FP >> 4]; + *ptr++ = hexchars[GDB_REGID_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(®s->a3, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + ssp = (unsigned long) (regs + 1); + *ptr++ = hexchars[GDB_REGID_SP >> 4]; + *ptr++ = hexchars[GDB_REGID_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(&ssp, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + +packet_waiting: + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) { + /* request repeat of last signal number */ + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + zero = 0; + ssp = (u32) (regs + 1); + ptr = output_buffer; + ptr = mem2hex(®s->d0, ptr, 4, 0); + ptr = mem2hex(®s->d1, ptr, 4, 0); + ptr = mem2hex(®s->d2, ptr, 4, 0); + ptr = mem2hex(®s->d3, ptr, 4, 0); + ptr = mem2hex(®s->a0, ptr, 4, 0); + ptr = mem2hex(®s->a1, ptr, 4, 0); + ptr = mem2hex(®s->a2, ptr, 4, 0); + ptr = mem2hex(®s->a3, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); /* 8 */ + ptr = mem2hex(®s->pc, ptr, 4, 0); + ptr = mem2hex(®s->mdr, ptr, 4, 0); + ptr = mem2hex(®s->epsw, ptr, 4, 0); + ptr = mem2hex(®s->lir, ptr, 4, 0); + ptr = mem2hex(®s->lar, ptr, 4, 0); + ptr = mem2hex(®s->mdrq, ptr, 4, 0); + + ptr = mem2hex(®s->e0, ptr, 4, 0); /* 15 */ + ptr = mem2hex(®s->e1, ptr, 4, 0); + ptr = mem2hex(®s->e2, ptr, 4, 0); + ptr = mem2hex(®s->e3, ptr, 4, 0); + ptr = mem2hex(®s->e4, ptr, 4, 0); + ptr = mem2hex(®s->e5, ptr, 4, 0); + ptr = mem2hex(®s->e6, ptr, 4, 0); + ptr = mem2hex(®s->e7, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); + ptr = mem2hex(®s, ptr, 4, 0); + ptr = mem2hex(®s->sp, ptr, 4, 0); + ptr = mem2hex(®s->mcrh, ptr, 4, 0); /* 26 */ + ptr = mem2hex(®s->mcrl, ptr, 4, 0); + ptr = mem2hex(®s->mcvf, ptr, 4, 0); + + ptr = mem2hex(&gdbstub_fpcr, ptr, 4, 0); /* 29 - FPCR */ + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + for (loop = 0; loop < 32; loop++) + ptr = mem2hex(&gdbstub_fpufs_array[loop], + ptr, 4, 0); /* 32 - FS0-31 */ + + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + { + const char *ptr; + + ptr = &input_buffer[1]; + ptr = hex2mem(ptr, ®s->d0, 4, 0); + ptr = hex2mem(ptr, ®s->d1, 4, 0); + ptr = hex2mem(ptr, ®s->d2, 4, 0); + ptr = hex2mem(ptr, ®s->d3, 4, 0); + ptr = hex2mem(ptr, ®s->a0, 4, 0); + ptr = hex2mem(ptr, ®s->a1, 4, 0); + ptr = hex2mem(ptr, ®s->a2, 4, 0); + ptr = hex2mem(ptr, ®s->a3, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); /* 8 */ + ptr = hex2mem(ptr, ®s->pc, 4, 0); + ptr = hex2mem(ptr, ®s->mdr, 4, 0); + ptr = hex2mem(ptr, ®s->epsw, 4, 0); + ptr = hex2mem(ptr, ®s->lir, 4, 0); + ptr = hex2mem(ptr, ®s->lar, 4, 0); + ptr = hex2mem(ptr, ®s->mdrq, 4, 0); + + ptr = hex2mem(ptr, ®s->e0, 4, 0); /* 15 */ + ptr = hex2mem(ptr, ®s->e1, 4, 0); + ptr = hex2mem(ptr, ®s->e2, 4, 0); + ptr = hex2mem(ptr, ®s->e3, 4, 0); + ptr = hex2mem(ptr, ®s->e4, 4, 0); + ptr = hex2mem(ptr, ®s->e5, 4, 0); + ptr = hex2mem(ptr, ®s->e6, 4, 0); + ptr = hex2mem(ptr, ®s->e7, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, ®s->sp, 4, 0); + ptr = hex2mem(ptr, ®s->mcrh, 4, 0); /* 26 */ + ptr = hex2mem(ptr, ®s->mcrl, 4, 0); + ptr = hex2mem(ptr, ®s->mcvf, 4, 0); + + ptr = hex2mem(ptr, &zero, 4, 0); /* 29 - FPCR */ + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + for (loop = 0; loop < 32; loop++) /* 32 - FS0-31 */ + ptr = hex2mem(ptr, &zero, 4, 0); + +#if 0 + /* + * See if the stack pointer has moved. If so, then copy + * the saved locals and ins to the new location. + */ + unsigned long *newsp = (unsigned long *) registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); +#endif + + gdbstub_strcpy(output_buffer, "OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) + ) { + if (mem2hex((char *) addr, output_buffer, + length, 1)) + break; + gdbstub_strcpy(output_buffer, "E03"); + } else { + gdbstub_strcpy(output_buffer, "E01"); + } + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA + * return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) && + *ptr++ == ':' + ) { + if (hex2mem(ptr, (char *) addr, length, 1)) + gdbstub_strcpy(output_buffer, "OK"); + else + gdbstub_strcpy(output_buffer, "E03"); + + gdbstub_flush_caches = 1; + } else { + gdbstub_strcpy(output_buffer, "E02"); + } + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no + * parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->pc = addr; + goto done; + + /* + * kill the program + */ + case 'k' : + goto done; /* just continue */ + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + /* + * Step to next instruction + */ + case 's': + /* + * using the T flag doesn't seem to perform single + * stepping (it seems to wind up being caught by the + * JTAG unit), so we have to use breakpoints and + * continue instead. + */ + if (gdbstub_single_step(regs) < 0) + /* ignore any fault error for now */ + gdbstub_printk("unable to set single-step" + " bp\n"); + goto done; + + /* + * Set baud rate (bBB) + */ + case 'b': + do { + int baudrate; + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) { + gdbstub_strcpy(output_buffer, "B01"); + break; + } + + if (baudrate) { + /* ACK before changing speed */ + putpacket("OK"); + gdbstub_io_set_baud(baudrate); + } + } while (0); + break; + + /* + * Set breakpoint + */ + case 'Z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_set_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + /* + * Clear breakpoint + */ + case 'z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_clear_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + default: + gdbstub_proto("### GDB Unsupported Cmd '%s'\n", + input_buffer); + break; + } + + /* reply to the request */ + putpacket(output_buffer); + } + +done: + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + if (gdbstub_flush_caches) + gdbstub_purge_cache(); + + gdbstub_load_fpu(); + mn10300_set_gdbleds(0); + if (excep == EXCEP_NMI) + NMICR = NMICR_NMIF; + + touch_softlockup_watchdog(); + + local_irq_restore(epsw); + return 1; +} + +/* + * handle event interception + */ +asmlinkage int gdbstub_intercept(struct pt_regs *regs, + enum exception_code excep) +{ + static u8 notfirst = 1; + int ret; + + if (gdbstub_busy) + gdbstub_printk("--> gdbstub reentered itself\n"); + gdbstub_busy = 1; + + if (notfirst) { + unsigned long mdr; + asm("mov mdr,%0" : "=d"(mdr)); + + gdbstub_entry( + "--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n", + regs, excep, mdr, regs->pc); + + gdbstub_entry( + "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, (unsigned long) &ret, + user_mode(regs) ? "User" : "Super"); + gdbstub_entry( + "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + gdbstub_entry( + "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + gdbstub_entry( + "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + gdbstub_entry( + "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + gdbstub_entry( + "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + gdbstub_entry( + "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + gdbstub_entry( + "threadinfo=%p task=%p)\n", + current_thread_info(), current); + } else { + notfirst = 1; + } + + ret = gdbstub(regs, excep); + + gdbstub_entry("<-- gdbstub_intercept()\n"); + gdbstub_busy = 0; + return ret; +} + +/* + * handle the GDB stub itself causing an exception + */ +asmlinkage void gdbstub_exception(struct pt_regs *regs, + enum exception_code excep) +{ + unsigned long mdr; + + asm("mov mdr,%0" : "=d"(mdr)); + gdbstub_entry("--> gdbstub exception({%p},%04x) [MDR=%lx]\n", + regs, excep, mdr); + + while ((unsigned long) regs == 0xffffffff) {} + + /* handle guarded memory accesses where we know it might fault */ + if (regs->pc == (unsigned) gdbstub_read_byte_guard) { + regs->pc = (unsigned) gdbstub_read_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_word_guard) { + regs->pc = (unsigned) gdbstub_read_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_dword_guard) { + regs->pc = (unsigned) gdbstub_read_dword_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_byte_guard) { + regs->pc = (unsigned) gdbstub_write_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_word_guard) { + regs->pc = (unsigned) gdbstub_write_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_dword_guard) { + regs->pc = (unsigned) gdbstub_write_dword_cont; + goto fault; + } + + gdbstub_printk("\n### GDB stub caused an exception ###\n"); + + /* something went horribly wrong */ + console_verbose(); + show_registers(regs); + + panic("GDB Stub caused an unexpected exception - can't continue\n"); + + /* we caught an attempt by the stub to access silly memory */ +fault: + gdbstub_entry("<-- gdbstub exception() = EFAULT\n"); + regs->d0 = -EFAULT; + return; +} + +/* + * send an exit message to GDB + */ +void gdbstub_exit(int status) +{ + unsigned char checksum; + unsigned char ch; + int count; + + gdbstub_busy = 1; + output_buffer[0] = 'W'; + output_buffer[1] = hexchars[(status >> 4) & 0x0F]; + output_buffer[2] = hexchars[status & 0x0F]; + output_buffer[3] = 0; + + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = output_buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hexchars[checksum >> 4]); + gdbstub_io_tx_char(hexchars[checksum & 0xf]); + + /* make sure the output is flushed, or else RedBoot might clobber it */ + gdbstub_io_tx_flush(); + + gdbstub_busy = 0; +} + +/* + * initialise the GDB stub + */ +asmlinkage void __init gdbstub_init(void) +{ +#ifdef CONFIG_GDBSTUB_IMMEDIATE + unsigned char ch; + int ret; +#endif + + gdbstub_busy = 1; + + printk(KERN_INFO "%s", gdbstub_banner); + + gdbstub_io_init(); + + gdbstub_entry("--> gdbstub_init\n"); + + /* try to talk to GDB (or anyone insane enough to want to type GDB + * protocol by hand) */ + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* 'hello world' */ + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + gdbstub_printk("GDB Stub waiting for packet\n"); + + /* in case GDB is started before us, ACK any packets that are already + * sitting there (presumably "$?#xx") + */ + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '$'); + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '#'); + /* eat first csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + /* eat second csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* NAK it */ + +#else + printk("GDB Stub ready\n"); +#endif + + gdbstub_busy = 0; + gdbstub_entry("<-- gdbstub_init\n"); +} + +/* + * register the console at a more appropriate time + */ +#ifdef CONFIG_GDBSTUB_CONSOLE +static int __init gdbstub_postinit(void) +{ + printk(KERN_NOTICE "registering console\n"); + register_console(&gdbstub_console); + return 0; +} + +__initcall(gdbstub_postinit); +#endif + +/* + * handle character reception on GDB serial port + * - jump into the GDB stub if BREAK is detected on the serial line + */ +asmlinkage void gdbstub_rx_irq(struct pt_regs *regs, enum exception_code excep) +{ + char ch; + int ret; + + gdbstub_entry("--> gdbstub_rx_irq\n"); + + do { + ret = gdbstub_io_rx_char(&ch, 1); + if (ret != -EIO && ret != -EAGAIN) { + if (ret != -EINTR) + gdbstub_rx_unget = ch; + gdbstub(regs, excep); + } + } while (ret != -EAGAIN); + + gdbstub_entry("<-- gdbstub_rx_irq\n"); +} diff --git a/arch/mn10300/kernel/head.S b/arch/mn10300/kernel/head.S new file mode 100644 index 00000000000..606bd8c6758 --- /dev/null +++ b/arch/mn10300/kernel/head.S @@ -0,0 +1,255 @@ +/* Boot entry point for MN10300 kernel + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/threads.h> +#include <linux/linkage.h> +#include <linux/serial_reg.h> +#include <asm/thread_info.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/frame.inc> +#include <asm/param.h> +#include <asm/unit/serial.h> + + .section .text.head,"ax" + +############################################################################### +# +# bootloader entry point +# +############################################################################### + .globl _start + .type _start,@function +_start: + # save commandline pointer + mov d0,a3 + + # preload the PGD pointer register + mov swapper_pg_dir,d0 + mov d0,(PTBR) + + # turn on the TLBs + mov MMUCTR_IIV|MMUCTR_DIV,d0 + mov d0,(MMUCTR) + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0 + mov d0,(MMUCTR) + + # turn on AM33v2 exception handling mode and set the trap table base + movhu (CPUP),d0 + or CPUP_EXM_AM33V2,d0 + movhu d0,(CPUP) + mov CONFIG_INTERRUPT_VECTOR_BASE,d0 + mov d0,(TBR) + + # invalidate and enable both of the caches + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + +#ifndef CONFIG_MN10300_CACHE_DISABLED +#ifdef CONFIG_MN10300_CACHE_WBACK +#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0 +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0 +#endif /* CACHE_DISABLED */ +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0 +#endif /* WBACK */ + movhu d0,(a0) # enable +#endif /* NOWRALLOC */ + + # turn on RTS on the debug serial port if applicable +#ifdef CONFIG_MN10300_UNIT_ASB2305 + bset UART_MCR_RTS,(ASB2305_DEBUG_MCR) +#endif + + # clear the BSS area + mov __bss_start,a0 + mov __bss_stop,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + mov d0,(a0) + inc4 a0 + bra bssclear +bssclear_end: + + # retrieve the parameters (including command line) before we overwrite + # them + cmp 0xabadcafe,d1 + bne __no_parameters + +__copy_parameters: + mov redboot_command_line,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + + mov redboot_platform_name,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 + mov d2,a3 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + +__no_parameters: + + # set up the registers with recognisable rubbish in them + mov init_thread_union+THREAD_SIZE-12,sp + + mov 0xea01eaea,d0 + mov d0,(4,sp) # EPSW save area + mov 0xea02eaea,d0 + mov d0,(8,sp) # PC save area + + mov 0xeb0060ed,d0 + mov d0,mdr + mov 0xeb0061ed,d0 + mov d0,mdrq + mov 0xeb0062ed,d0 + mov d0,mcrh + mov 0xeb0063ed,d0 + mov d0,mcrl + mov 0xeb0064ed,d0 + mov d0,mcvf + mov 0xed0065ed,a3 + mov a3,usp + + mov 0xed00e0ed,e0 + mov 0xed00e1ed,e1 + mov 0xed00e2ed,e2 + mov 0xed00e3ed,e3 + mov 0xed00e4ed,e4 + mov 0xed00e5ed,e5 + mov 0xed00e6ed,e6 + mov 0xed00e7ed,e7 + + mov 0xed00d0ed,d0 + mov 0xed00d1ed,d1 + mov 0xed00d2ed,d2 + mov 0xed00d3ed,d3 + mov 0xed00a0ed,a0 + mov 0xed00a1ed,a1 + mov 0xed00a2ed,a2 + mov 0,a3 + + # set up the initial kernel stack + SAVE_ALL + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + # put different recognisable rubbish in the regs + mov 0xfb0060ed,d0 + mov d0,mdr + mov 0xfb0061ed,d0 + mov d0,mdrq + mov 0xfb0062ed,d0 + mov d0,mcrh + mov 0xfb0063ed,d0 + mov d0,mcrl + mov 0xfb0064ed,d0 + mov d0,mcvf + mov 0xfd0065ed,a0 + mov a0,usp + + mov 0xfd00e0ed,e0 + mov 0xfd00e1ed,e1 + mov 0xfd00e2ed,e2 + mov 0xfd00e3ed,e3 + mov 0xfd00e4ed,e4 + mov 0xfd00e5ed,e5 + mov 0xfd00e6ed,e6 + mov 0xfd00e7ed,e7 + + mov 0xfd00d0ed,d0 + mov 0xfd00d1ed,d1 + mov 0xfd00d2ed,d2 + mov 0xfd00d3ed,d3 + mov 0xfd00a0ed,a0 + mov 0xfd00a1ed,a1 + mov 0xfd00a2ed,a2 + + # we may be holding current in E2 +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov init_task,e2 +#endif + + # initialise the processor and the unit + call processor_init[],0 + call unit_init[],0 + +#ifdef CONFIG_GDBSTUB + call gdbstub_init[],0 + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + .globl __gdbstub_pause +__gdbstub_pause: + bra __gdbstub_pause +#endif +#endif + + jmp start_kernel + .size _start, _start-. +ENTRY(__head_end) + +/* + * This is initialized to disallow all access to the low 2G region + * - the high 2G region is managed directly by the MMU + * - range 0x70000000-0x7C000000 are initialised for use by VMALLOC + */ + .section .bss + .balign PAGE_SIZE +ENTRY(swapper_pg_dir) + .space PTRS_PER_PGD*4 + +/* + * The page tables are initialized to only 8MB here - the final page + * tables are set up later depending on memory size. + */ + + .balign PAGE_SIZE +ENTRY(empty_zero_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_pte_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(large_page_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(kernel_vmalloc_ptes) + .space ((VMALLOC_END-VMALLOC_START)/PAGE_SIZE)*4 diff --git a/arch/mn10300/kernel/init_task.c b/arch/mn10300/kernel/init_task.c new file mode 100644 index 00000000000..39fe6882dd1 --- /dev/null +++ b/arch/mn10300/kernel/init_task.c @@ -0,0 +1,45 @@ +/* MN10300 Initial task definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); +EXPORT_SYMBOL(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is THREAD_SIZE aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h new file mode 100644 index 00000000000..eee2eee8626 --- /dev/null +++ b/arch/mn10300/kernel/internal.h @@ -0,0 +1,20 @@ +/* Internal definitions for the arch part of the core kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * kthread.S + */ +extern int kernel_thread_helper(int); + +/* + * entry.S + */ +extern void ret_from_fork(struct task_struct *) __attribute__((noreturn)); diff --git a/arch/mn10300/kernel/io.c b/arch/mn10300/kernel/io.c new file mode 100644 index 00000000000..e96fdf6bb54 --- /dev/null +++ b/arch/mn10300/kernel/io.c @@ -0,0 +1,30 @@ +/* MN10300 Misaligned multibyte-word I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <asm/io.h> + +/* + * output data from a potentially misaligned buffer + */ +void __outsl(unsigned long addr, const void *buffer, int count) +{ + const unsigned char *buf = buffer; + unsigned long val; + + while (count--) { + memcpy(&val, buf, 4); + outl(val, addr); + buf += 4; + } +} +EXPORT_SYMBOL(__outsl); diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c new file mode 100644 index 00000000000..761c434a248 --- /dev/null +++ b/arch/mn10300/kernel/irq.c @@ -0,0 +1,235 @@ +/* MN10300 Arch-specific interrupt handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/seq_file.h> +#include <asm/setup.h> + +unsigned long __mn10300_irq_enabled_epsw = EPSW_IE | EPSW_IM_7; +EXPORT_SYMBOL(__mn10300_irq_enabled_epsw); + +atomic_t irq_err_count; + +/* + * MN10300 INTC controller operations + */ +static void mn10300_cpupic_disable(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_enable(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_ack(unsigned int irq) +{ + u16 tmp; + *(volatile u8 *) &GxICR(irq) = GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_mask(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL); + tmp = GxICR(irq); +} + +static void mn10300_cpupic_mask_ack(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_unmask(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_end(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; + tmp = GxICR(irq); +} + +static struct irq_chip mn10300_cpu_pic = { + .name = "cpu", + .disable = mn10300_cpupic_disable, + .enable = mn10300_cpupic_enable, + .ack = mn10300_cpupic_ack, + .mask = mn10300_cpupic_mask, + .mask_ack = mn10300_cpupic_mask_ack, + .unmask = mn10300_cpupic_unmask, + .end = mn10300_cpupic_end, +}; + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(int irq) +{ + printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq); +} + +/* + * change the level at which an IRQ executes + * - must not be called whilst interrupts are being processed! + */ +void set_intr_level(int irq, u16 level) +{ + u16 tmp; + + if (in_interrupt()) + BUG(); + + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_ENABLE) | level; + tmp = GxICR(irq); +} + +/* + * mark an interrupt to be ACK'd after interrupt handlers have been run rather + * than before + * - see Documentation/mn10300/features.txt + */ +void set_intr_postackable(int irq) +{ + set_irq_handler(irq, handle_level_irq); +} + +/* + * initialise the interrupt system + */ +void __init init_IRQ(void) +{ + int irq; + + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_desc[irq].chip == &no_irq_type) + set_irq_chip_and_handler(irq, &mn10300_cpu_pic, + handle_edge_irq); + unit_init_IRQ(); +} + +/* + * handle normal device IRQs + */ +asmlinkage void do_IRQ(void) +{ + unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw; + int irq; + + sp = current_stack_pointer(); + if (sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN) + BUG(); + + /* make sure local_irq_enable() doesn't muck up the interrupt priority + * setting in EPSW */ + old_irq_enabled_epsw = __mn10300_irq_enabled_epsw; + local_save_flags(epsw); + __mn10300_irq_enabled_epsw = EPSW_IE | (EPSW_IM & epsw); + irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL; + + __IRQ_STAT(smp_processor_id(), __irq_count)++; + + irq_enter(); + + for (;;) { + /* ask the interrupt controller for the next IRQ to process + * - the result we get depends on EPSW.IM + */ + irq = IAGR & IAGR_GN; + if (!irq) + break; + + local_irq_restore(irq_disabled_epsw); + + generic_handle_irq(irq >> 2); + + /* restore IRQ controls for IAGR access */ + local_irq_restore(epsw); + } + + __mn10300_irq_enabled_epsw = old_irq_enabled_epsw; + + irq_exit(); +} + +/* + * Display interrupt management information through /proc/interrupts + */ +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j, cpu; + struct irqaction *action; + unsigned long flags; + + switch (i) { + /* display column title bar naming CPUs */ + case 0: + seq_printf(p, " "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "CPU%d ", j); + seq_putc(p, '\n'); + break; + + /* display information rows, one per active CPU */ + case 1 ... NR_IRQS - 1: + spin_lock_irqsave(&irq_desc[i].lock, flags); + + action = irq_desc[i].action; + if (action) { + seq_printf(p, "%3d: ", i); + for_each_present_cpu(cpu) + seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]); + seq_printf(p, " %14s.%u", irq_desc[i].chip->name, + (GxICR(i) & GxICR_LEVEL) >> + GxICR_LEVEL_SHIFT); + seq_printf(p, " %s", action->name); + + for (action = action->next; + action; + action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); + } + + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + break; + + /* polish off with NMI and error counters */ + case NR_IRQS: + seq_printf(p, "NMI: "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); + + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + break; + } + + return 0; +} diff --git a/arch/mn10300/kernel/kernel_execve.S b/arch/mn10300/kernel/kernel_execve.S new file mode 100644 index 00000000000..86039f10526 --- /dev/null +++ b/arch/mn10300/kernel/kernel_execve.S @@ -0,0 +1,37 @@ +/* MN10300 In-kernel program execution + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/linkage.h> +#include <asm/unistd.h> + +############################################################################### +# +# Do a system call from kernel instead of calling sys_execve so we end up with +# proper pt_regs. +# +# int kernel_execve(const char *filename, char *const argv[], +# char *const envp[]) +# +# On entry: D0/D1/8(SP): arguments to function +# On return: D0: syscall return. +# +############################################################################### + .globl kernel_execve + .type kernel_execve,@function +kernel_execve: + mov a3,a1 + mov d0,a0 + mov (12,sp),a3 + mov +__NR_execve,d0 + syscall 0 + mov a1,a3 + rets + + .size kernel_execve,.-kernel_execve diff --git a/arch/mn10300/kernel/kprobes.c b/arch/mn10300/kernel/kprobes.c new file mode 100644 index 00000000000..dacafab00eb --- /dev/null +++ b/arch/mn10300/kernel/kprobes.c @@ -0,0 +1,653 @@ +/* MN10300 Kernel probes implementation + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * 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 Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include <linux/spinlock.h> +#include <linux/preempt.h> +#include <linux/kdebug.h> +#include <asm/cacheflush.h> + +struct kretprobe_blackpoint kretprobe_blacklist[] = { { NULL, NULL } }; +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long current_kprobe_orig_pc; +static unsigned long current_kprobe_next_pc; +static int current_kprobe_ss_flags; +static unsigned long kprobe_status; +static kprobe_opcode_t current_kprobe_ss_buf[MAX_INSN_SIZE + 2]; +static unsigned long current_kprobe_bp_addr; + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; + + +/* singlestep flag bits */ +#define SINGLESTEP_BRANCH 1 +#define SINGLESTEP_PCREL 2 + +#define READ_BYTE(p, valp) \ + do { *(u8 *)(valp) = *(u8 *)(p); } while (0) + +#define READ_WORD16(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + } while (0) + +#define READ_WORD32(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + READ_BYTE((u8 *)(p) + 2, (u8 *)(valp) + 2); \ + READ_BYTE((u8 *)(p) + 3, (u8 *)(valp) + 3); \ + } while (0) + + +static const u8 mn10300_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +#define LT (1 << 0) +#define GT (1 << 1) +#define GE (1 << 2) +#define LE (1 << 3) +#define CS (1 << 4) +#define HI (1 << 5) +#define CC (1 << 6) +#define LS (1 << 7) +#define EQ (1 << 8) +#define NE (1 << 9) +#define RA (1 << 10) +#define VC (1 << 11) +#define VS (1 << 12) +#define NC (1 << 13) +#define NS (1 << 14) + +static const u16 cond_table[] = { + /* V C N Z */ + /* 0 0 0 0 */ (NE | NC | CC | VC | GE | GT | HI), + /* 0 0 0 1 */ (EQ | NC | CC | VC | GE | LE | LS), + /* 0 0 1 0 */ (NE | NS | CC | VC | LT | LE | HI), + /* 0 0 1 1 */ (EQ | NS | CC | VC | LT | LE | LS), + /* 0 1 0 0 */ (NE | NC | CS | VC | GE | GT | LS), + /* 0 1 0 1 */ (EQ | NC | CS | VC | GE | LE | LS), + /* 0 1 1 0 */ (NE | NS | CS | VC | LT | LE | LS), + /* 0 1 1 1 */ (EQ | NS | CS | VC | LT | LE | LS), + /* 1 0 0 0 */ (NE | NC | CC | VS | LT | LE | HI), + /* 1 0 0 1 */ (EQ | NC | CC | VS | LT | LE | LS), + /* 1 0 1 0 */ (NE | NS | CC | VS | GE | GT | HI), + /* 1 0 1 1 */ (EQ | NS | CC | VS | GE | LE | LS), + /* 1 1 0 0 */ (NE | NC | CS | VS | LT | LE | LS), + /* 1 1 0 1 */ (EQ | NC | CS | VS | LT | LE | LS), + /* 1 1 1 0 */ (NE | NS | CS | VS | GE | GT | LS), + /* 1 1 1 1 */ (EQ | NS | CS | VS | GE | LE | LS), +}; + +/* + * Calculate what the PC will be after executing next instruction + */ +static unsigned find_nextpc(struct pt_regs *regs, int *flags) +{ + unsigned size; + s8 x8; + s16 x16; + s32 x32; + u8 opc, *pc, *sp, *next; + + next = 0; + *flags = SINGLESTEP_PCREL; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + opc = *pc; + + size = mn10300_insn_sizes[opc]; + if (size > 0) { + next = pc + size; + } else { + switch (opc) { + /* Bxx (d8,PC) */ + case 0xc0 ... 0xca: + x8 = 2; + if (cond_table[regs->epsw & 0xf] & (1 << (opc & 0xf))) + x8 = (s8)pc[1]; + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + READ_WORD16(pc + 1, &x16); + next = pc + x16; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + READ_WORD32(pc + 1, &x32); + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RETF */ + case 0xde: + next = (u8 *)regs->mdr; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RET */ + case 0xdf: + sp += pc[2]; + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (opc & 3) { + case 0: + next = (u8 *)regs->a0; + break; + case 1: + next = (u8 *)regs->a1; + break; + case 2: + next = (u8 *)regs->a2; + break; + case 3: + next = (u8 *)regs->a3; + break; + } + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfc) { + /* RETS */ + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfd) { + /* RTI */ + READ_WORD32(sp + 4, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + next = pc + 3; + opc = pc[1]; + if (opc >= 0xe8 && opc <= 0xeb && + (cond_table[regs->epsw & 0xf] & + (1 << ((opc & 0xf) + 3))) + ) { + READ_BYTE(pc+2, &x8); + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + } + break; + + case 0xfa: + if (pc[1] == 0xff) { + /* CALLS (d16,PC) */ + READ_WORD16(pc + 2, &x16); + next = pc + x16; + } else + next = pc + 4; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xfc: + x32 = 6; + if (pc[1] == 0xff) { + /* CALLS (d32,PC) */ + READ_WORD32(pc + 2, &x32); + } + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + /* LXX (d8,PC) */ + /* SETLB - loads the next four bytes into the LIR reg */ + case 0xd0 ... 0xda: + case 0xdb: + panic("Can't singlestep Lxx/SETLB\n"); + break; + } + } + return (unsigned)next; + +} + +/* + * set up out of place singlestep of some branching instructions + */ +static unsigned __kprobes singlestep_branch_setup(struct pt_regs *regs) +{ + u8 opc, *pc, *sp, *next; + + next = NULL; + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + + switch (pc[0]) { + case 0xc0 ... 0xca: /* Bxx (d8,PC) */ + case 0xcc: /* JMP (d16,PC) */ + case 0xdc: /* JMP (d32,PC) */ + case 0xf8: /* Bxx (d8,PC) 3-byte version */ + /* don't really need to do anything except cause trap */ + next = pc; + break; + + case 0xcd: /* CALL (d16,PC) */ + pc[1] = 5; + pc[2] = 0; + next = pc + 5; + break; + + case 0xdd: /* CALL (d32,PC) */ + pc[1] = 7; + pc[2] = 0; + pc[3] = 0; + pc[4] = 0; + next = pc + 7; + break; + + case 0xde: /* RETF */ + next = pc + 3; + regs->mdr = (unsigned) next; + break; + + case 0xdf: /* RET */ + sp += pc[2]; + next = pc + 3; + *(unsigned *)sp = (unsigned) next; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf3) { + /* CALLS (An) */ + /* use CALLS (d16,PC) to avoid mucking with An */ + pc[0] = 0xfa; + pc[1] = 0xff; + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + } else if (opc >= 0xf4 && opc <= 0xf7) { + /* JMP (An) */ + next = pc; + } else if (opc == 0xfc) { + /* RETS */ + next = pc + 2; + *(unsigned *) sp = (unsigned) next; + } else if (opc == 0xfd) { + /* RTI */ + next = pc + 2; + *(unsigned *)(sp + 4) = (unsigned) next; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + pc[2] = 6; + pc[3] = 0; + pc[4] = 0; + pc[5] = 0; + next = pc + 6; + break; + + case 0xd0 ... 0xda: /* LXX (d8,PC) */ + case 0xdb: /* SETLB */ + panic("Can't singlestep Lxx/SETLB\n"); + } + + return (unsigned) next; +} + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + return 0; +} + +void __kprobes arch_copy_kprobe(struct kprobe *p) +{ + memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE); +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + mn10300_dcache_flush(); + mn10300_icache_inv(); +} + +void arch_remove_kprobe(struct kprobe *p) +{ +} + +static inline +void __kprobes disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->pc = (unsigned long) p->addr; + mn10300_dcache_flush(); + mn10300_icache_inv(); +} + +static inline +void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long nextpc; + + current_kprobe_orig_pc = regs->pc; + memcpy(current_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE); + regs->pc = (unsigned long) current_kprobe_ss_buf; + + nextpc = find_nextpc(regs, ¤t_kprobe_ss_flags); + if (current_kprobe_ss_flags & SINGLESTEP_PCREL) + current_kprobe_next_pc = + current_kprobe_orig_pc + (nextpc - regs->pc); + else + current_kprobe_next_pc = nextpc; + + /* branching instructions need special handling */ + if (current_kprobe_ss_flags & SINGLESTEP_BRANCH) + nextpc = singlestep_branch_setup(regs); + + current_kprobe_bp_addr = nextpc; + + *(u8 *) nextpc = BREAKPOINT_INSTRUCTION; + mn10300_dcache_flush_range2((unsigned) current_kprobe_ss_buf, + sizeof(current_kprobe_ss_buf)); + mn10300_icache_inv(); +} + +static inline int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + unsigned int *addr = (unsigned int *) regs->pc; + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) + goto ss_probe; + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + p = get_kprobe(addr); + if (!p) { + if (*addr != BREAKPOINT_INSTRUCTION) { + /* The breakpoint instruction was removed right after + * we hit it. Another cpu has removed either a + * probepoint or a debugger breakpoint at this address. + * In either case, no further handling of this + * interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + if (p->pre_handler(p, regs)) { + /* handler has already set things up, so skip ss setup */ + return 1; + } + +ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "breakpoint" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->ainsn.insn. + */ +static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + /* we may need to fixup regs/stack after singlestepping a call insn */ + if (current_kprobe_ss_flags & SINGLESTEP_BRANCH) { + regs->pc = current_kprobe_orig_pc; + switch (p->ainsn.insn[0]) { + case 0xcd: /* CALL (d16,PC) */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 5; + break; + case 0xdd: /* CALL (d32,PC) */ + /* fixup mdr and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 7; + break; + case 0xf0: + if (p->ainsn.insn[1] >= 0xf0 && + p->ainsn.insn[1] <= 0xf3) { + /* CALLS (An) */ + /* fixup MDR and return address on stack */ + regs->mdr = regs->pc + 2; + *(unsigned *) regs->sp = regs->mdr; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 6; + break; + } + } + + regs->pc = current_kprobe_next_pc; + current_kprobe_bp_addr = 0; +} + +static inline int __kprobes post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + resume_execution(current_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline +int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler && + current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + + switch (val) { + case DIE_BREAKPOINT: + if (current_kprobe_bp_addr != args->regs->pc) { + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + } else { + if (post_kprobe_handler(args->regs)) + return NOTIFY_STOP; + } + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + default: + break; + } + return NOTIFY_DONE; +} + +/* Jprobes support. */ +static struct pt_regs jprobe_saved_regs; +static struct pt_regs *jprobe_saved_regs_location; +static kprobe_opcode_t jprobe_saved_stack[MAX_STACK_SIZE]; + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + + jprobe_saved_regs_location = regs; + memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs)); + + /* Save a whole stack frame, this gets arguments + * pushed onto the stack after using up all the + * arg registers. + */ + memcpy(&jprobe_saved_stack, regs + 1, sizeof(jprobe_saved_stack)); + + /* setup return addr to the jprobe handler routine */ + regs->pc = (unsigned long) jp->entry; + return 1; +} + +void __kprobes jprobe_return(void) +{ + void *orig_sp = jprobe_saved_regs_location + 1; + + preempt_enable_no_resched(); + asm volatile(" mov %0,sp\n" + ".globl jprobe_return_bp_addr\n" + "jprobe_return_bp_addr:\n\t" + " .byte 0xff\n" + : : "d" (orig_sp)); +} + +extern void jprobe_return_bp_addr(void); + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u8 *addr = (u8 *) regs->pc; + + if (addr == (u8 *) jprobe_return_bp_addr) { + if (jprobe_saved_regs_location != regs) { + printk(KERN_ERR"JPROBE:" + " Current regs (%p) does not match saved regs" + " (%p).\n", + regs, jprobe_saved_regs_location); + BUG(); + } + + /* Restore old register state. + */ + memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); + + memcpy(regs + 1, &jprobe_saved_stack, + sizeof(jprobe_saved_stack)); + return 1; + } + return 0; +} + +int __init arch_init_kprobes(void) +{ + return 0; +} diff --git a/arch/mn10300/kernel/kthread.S b/arch/mn10300/kernel/kthread.S new file mode 100644 index 00000000000..b5ae467ac5e --- /dev/null +++ b/arch/mn10300/kernel/kthread.S @@ -0,0 +1,31 @@ +/* MN10300 Kernel thread trampoline function + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .text + +############################################################################### +# +# kernel_thread_helper - trampoline for kernel_thread() +# +# On entry: +# A2 = address of function to call +# D2 = function argument +# +############################################################################### + .globl kernel_thread_helper + .type kernel_thread_helper,@function +kernel_thread_helper: + mov do_exit,d1 + mov d1,(sp) + mov d1,mdr + mov d2,d0 + jmp (a2) + + .size kernel_thread_helper,.-kernel_thread_helper diff --git a/arch/mn10300/kernel/mn10300-debug.c b/arch/mn10300/kernel/mn10300-debug.c new file mode 100644 index 00000000000..bd8196478cb --- /dev/null +++ b/arch/mn10300/kernel/mn10300-debug.c @@ -0,0 +1,58 @@ +/* Debugging stuff for the MN10300-based processors + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <asm/serial-regs.h> + +#undef MN10300_CONSOLE_ON_SERIO + +/* + * write a string directly through one of the serial ports on-board the MN10300 + */ +#ifdef MN10300_CONSOLE_ON_SERIO +void debug_to_serial_mnser(const char *p, int n) +{ + char ch; + + for (; n > 0; n--) { + ch = *p++; + +#if MN10300_CONSOLE_ON_SERIO == 0 + while (SC0STR & (SC01STR_TBF)) continue; + SC0TXB = ch; + while (SC0STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 1 + while (SC1STR & (SC01STR_TBF)) continue; + SC1TXB = ch; + while (SC1STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 2 + while (SC2STR & (SC2STR_TBF)) continue; + SC2TXB = ch; + while (SC2STR & (SC2STR_TBF)) continue; + if (ch == 0x0a) { + SC2TXB = 0x0d; + while (SC2STR & (SC2STR_TBF)) continue; + } + +#endif + } +} +#endif + diff --git a/arch/mn10300/kernel/mn10300-serial-low.S b/arch/mn10300/kernel/mn10300-serial-low.S new file mode 100644 index 00000000000..ef3f4c1df2a --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial-low.S @@ -0,0 +1,191 @@ +############################################################################### +# +# Virtual DMA driver for MN10300 serial ports +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/page.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/timer-regs.h> +#include <asm/proc/cache.h> +#include <asm/unit/timex.h> +#include "mn10300-serial.h" + +#define SCxCTR 0x00 +#define SCxICR 0x04 +#define SCxTXB 0x08 +#define SCxRXB 0x09 +#define SCxSTR 0x0c +#define SCxTIM 0x0d + + .text + +############################################################################### +# +# serial port interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_interrupt) + or EPSW_IE,psw # permit overriding by + # debugging interrupts + movm [d2,d3,a2,a3,exreg0],(sp) + + movhu (IAGR),a2 # see if which interrupt is + # pending + and IAGR_GN,a2 + add a2,a2 + add mn10300_serial_int_tbl,a2 + + mov (a2+),a3 + mov (__iobase,a3),e2 + mov (a2),a2 + jmp (a2) + +############################################################################### +# +# serial port receive interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - stores data/status byte pairs in the ring buffer +# - induces a scheduler tick timer interrupt when done, which we then subvert +# on entry: +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### +ENTRY(mn10300_serial_vdma_rx_handler) + mov (__rx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + mov (__rx_inp,a3),d3 + mov d3,a2 + add 2,d3 + and MNSC_BUFFER_SIZE-1,d3 + mov (__rx_outp,a3),d2 + cmp d3,d2 + beq mnsc_vdma_rx_overflow + + mov (__rx_buffer,a3),d2 + add d2,a2 + movhu (SCxSTR,e2),d2 + movbu d2,(1,a2) + movbu (SCxRXB,e2),d2 + movbu d2,(a2) + mov d3,(__rx_inp,a3) + bset MNSCx_RX_AVAIL,(__intr_flags,a3) + +mnsc_vdma_rx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_rx_overflow: + bset MNSCx_RX_OVERF,(__intr_flags,a3) + bra mnsc_vdma_rx_done + +############################################################################### +# +# serial port transmit interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - retrieves data bytes from the ring buffer and passes them to the serial port +# - induces a scheduler tick timer interrupt when done, which we then subvert +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_tx_handler) + mov (__tx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + btst 0x01,(__tx_break,a3) # handle transmit break request + bne mnsc_vdma_tx_break + + movbu (SCxSTR,e2),d2 # don't try and transmit a char if the + # buffer is not empty + btst SC01STR_TBF,d2 # (may have tried to jumpstart) + bne mnsc_vdma_tx_noint + + movbu (__tx_xchar,a3),d2 # handle hi-pri XON/XOFF + or d2,d2 + bne mnsc_vdma_tx_xchar + + mov (__tx_info_buffer,a3),a2 # get the uart_info struct for Tx + mov (__xmit_tail,a2),d3 + mov (__xmit_head,a2),d2 + cmp d3,d2 + beq mnsc_vdma_tx_empty + + mov (__xmit_buffer,a2),d2 # get a char from the buffer and + # transmit it + movbu (d3,d2),d2 + movbu d2,(SCxTXB,e2) # Tx + + inc d3 # advance the buffer pointer + and __UART_XMIT_SIZE-1,d3 + mov (__xmit_head,a2),d2 + mov d3,(__xmit_tail,a2) + + sub d3,d2 # see if we've written everything + beq mnsc_vdma_tx_empty + + and __UART_XMIT_SIZE-1,d2 # see if we just made a hole + cmp __UART_XMIT_SIZE-2,d2 + beq mnsc_vdma_tx_made_hole + +mnsc_vdma_tx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + +mnsc_vdma_tx_noint: + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_tx_empty: + mov +(GxICR_LEVEL_1|GxICR_DETECT),d2 + movhu d2,(e3) # disable the interrupt + movhu (e3),d2 # flush + + bset MNSCx_TX_EMPTY,(__intr_flags,a3) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_break: + movhu (SCxCTR,e2),d2 # turn on break mode + or SC01CTR_BKE,d2 + movhu d2,(SCxCTR,e2) + mov +(GxICR_LEVEL_1|GxICR_DETECT),d2 + movhu d2,(e3) # disable transmit interrupts on this + # channel + movhu (e3),d2 # flush + bra mnsc_vdma_tx_noint + +mnsc_vdma_tx_xchar: + bclr 0xff,(__tx_xchar,a3) + movbu d2,(SCxTXB,e2) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_made_hole: + bset MNSCx_TX_SPACE,(__intr_flags,a3) + bra mnsc_vdma_tx_done diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c new file mode 100644 index 00000000000..b9c268c6b2f --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -0,0 +1,1480 @@ +/* MN10300 On-chip serial port UART driver + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static const char serial_name[] = "MN10300 Serial driver"; +static const char serial_version[] = "mn10300_serial-1.0"; +static const char serial_revdate[] = "2007-11-06"; + +#if defined(CONFIG_MN10300_TTYSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/circ_buf.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/serial-regs.h> +#include <asm/unit/timex.h> +#include "mn10300-serial.h" + +static inline __attribute__((format(printf, 1, 2))) +void no_printk(const char *fmt, ...) +{ +} + +#define kenter(FMT, ...) \ + printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define _enter(FMT, ...) \ + no_printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define kdebug(FMT, ...) \ + printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define _debug(FMT, ...) \ + no_printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define kproto(FMT, ...) \ + printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) +#define _proto(FMT, ...) \ + no_printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) + +#define NR_UARTS 3 + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count); +static int __init mn10300_serial_console_setup(struct console *co, + char *options); + +static struct uart_driver mn10300_serial_driver; +static struct console mn10300_serial_console = { + .name = "ttySM", + .write = mn10300_serial_console_write, + .device = uart_console_device, + .setup = mn10300_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mn10300_serial_driver, +}; +#endif + +static struct uart_driver mn10300_serial_driver = { + .owner = NULL, + .driver_name = "mn10300-serial", + .dev_name = "ttySM", + .major = TTY_MAJOR, + .minor = 128, + .nr = NR_UARTS, +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + .cons = &mn10300_serial_console, +#endif +}; + +static unsigned int mn10300_serial_tx_empty(struct uart_port *); +static void mn10300_serial_set_mctrl(struct uart_port *, unsigned int mctrl); +static unsigned int mn10300_serial_get_mctrl(struct uart_port *); +static void mn10300_serial_stop_tx(struct uart_port *); +static void mn10300_serial_start_tx(struct uart_port *); +static void mn10300_serial_send_xchar(struct uart_port *, char ch); +static void mn10300_serial_stop_rx(struct uart_port *); +static void mn10300_serial_enable_ms(struct uart_port *); +static void mn10300_serial_break_ctl(struct uart_port *, int ctl); +static int mn10300_serial_startup(struct uart_port *); +static void mn10300_serial_shutdown(struct uart_port *); +static void mn10300_serial_set_termios(struct uart_port *, + struct ktermios *new, + struct ktermios *old); +static const char *mn10300_serial_type(struct uart_port *); +static void mn10300_serial_release_port(struct uart_port *); +static int mn10300_serial_request_port(struct uart_port *); +static void mn10300_serial_config_port(struct uart_port *, int); +static int mn10300_serial_verify_port(struct uart_port *, + struct serial_struct *); + +static const struct uart_ops mn10300_serial_ops = { + .tx_empty = mn10300_serial_tx_empty, + .set_mctrl = mn10300_serial_set_mctrl, + .get_mctrl = mn10300_serial_get_mctrl, + .stop_tx = mn10300_serial_stop_tx, + .start_tx = mn10300_serial_start_tx, + .send_xchar = mn10300_serial_send_xchar, + .stop_rx = mn10300_serial_stop_rx, + .enable_ms = mn10300_serial_enable_ms, + .break_ctl = mn10300_serial_break_ctl, + .startup = mn10300_serial_startup, + .shutdown = mn10300_serial_shutdown, + .set_termios = mn10300_serial_set_termios, + .type = mn10300_serial_type, + .release_port = mn10300_serial_release_port, + .request_port = mn10300_serial_request_port, + .config_port = mn10300_serial_config_port, + .verify_port = mn10300_serial_verify_port, +}; + +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id); + +/* + * the first on-chip serial port: ttySM0 (aka SIF0) + */ +#ifdef CONFIG_MN10300_TTYSM0 +struct mn10300_serial_port mn10300_serial_port_sif0 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC0CTR, + .uart.mapbase = (unsigned long) &SC0CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 0, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif0.uart.lock), + .name = "ttySM0", + ._iobase = &SC0CTR, + ._control = &SC0CTR, + ._status = (volatile u8 *) &SC0STR, + ._intr = &SC0ICR, + ._rxb = &SC0RXB, + ._txb = &SC0TXB, + .rx_name = "ttySM0/Rx", + .tx_name = "ttySM0/Tx", +#ifdef CONFIG_MN10300_TTYSM0_TIMER8 + .tm_name = "ttySM0/Timer8", + ._tmxmd = &TM8MD, + ._tmxbr = &TM8BR, + ._tmicr = &TM8ICR, + .tm_irq = TM8IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#else /* CONFIG_MN10300_TTYSM0_TIMER2 */ + .tm_name = "ttySM0/Timer2", + ._tmxmd = &TM2MD, + ._tmxbr = (volatile u16 *) &TM2BR, + ._tmicr = &TM2ICR, + .tm_irq = TM2IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#endif + .rx_irq = SC0RXIRQ, + .tx_irq = SC0TXIRQ, + .rx_icr = &GxICR(SC0RXIRQ), + .tx_icr = &GxICR(SC0TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM0 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM0 */ + +/* + * the second on-chip serial port: ttySM1 (aka SIF1) + */ +#ifdef CONFIG_MN10300_TTYSM1 +struct mn10300_serial_port mn10300_serial_port_sif1 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC1CTR, + .uart.mapbase = (unsigned long) &SC1CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 1, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif1.uart.lock), + .name = "ttySM1", + ._iobase = &SC1CTR, + ._control = &SC1CTR, + ._status = (volatile u8 *) &SC1STR, + ._intr = &SC1ICR, + ._rxb = &SC1RXB, + ._txb = &SC1TXB, + .rx_name = "ttySM1/Rx", + .tx_name = "ttySM1/Tx", +#ifdef CONFIG_MN10300_TTYSM1_TIMER9 + .tm_name = "ttySM1/Timer9", + ._tmxmd = &TM9MD, + ._tmxbr = &TM9BR, + ._tmicr = &TM9ICR, + .tm_irq = TM9IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#else /* CONFIG_MN10300_TTYSM1_TIMER3 */ + .tm_name = "ttySM1/Timer3", + ._tmxmd = &TM3MD, + ._tmxbr = (volatile u16 *) &TM3BR, + ._tmicr = &TM3ICR, + .tm_irq = TM3IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#endif + .rx_irq = SC1RXIRQ, + .tx_irq = SC1TXIRQ, + .rx_icr = &GxICR(SC1RXIRQ), + .tx_icr = &GxICR(SC1TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM1 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM1 */ + +/* + * the third on-chip serial port: ttySM2 (aka SIF2) + */ +#ifdef CONFIG_MN10300_TTYSM2 +struct mn10300_serial_port mn10300_serial_port_sif2 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC2CTR, + .uart.mapbase = (unsigned long) &SC2CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 2, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .uart.type = PORT_MN10300_CTS, +#else + .uart.type = PORT_MN10300, +#endif + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif2.uart.lock), + .name = "ttySM2", + .rx_name = "ttySM2/Rx", + .tx_name = "ttySM2/Tx", + .tm_name = "ttySM2/Timer10", + ._iobase = &SC2CTR, + ._control = &SC2CTR, + ._status = &SC2STR, + ._intr = &SC2ICR, + ._rxb = &SC2RXB, + ._txb = &SC2TXB, + ._tmxmd = &TM10MD, + ._tmxbr = &TM10BR, + ._tmicr = &TM10ICR, + .tm_irq = TM10IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, + .rx_irq = SC2RXIRQ, + .tx_irq = SC2TXIRQ, + .rx_icr = &GxICR(SC2RXIRQ), + .tx_icr = &GxICR(SC2TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .options = MNSCx_OPT_CTS, +#else + .options = 0, +#endif +#ifdef CONFIG_GDBSTUB_ON_TTYSM2 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM2 */ + + +/* + * list of available serial ports + */ +struct mn10300_serial_port *mn10300_serial_ports[NR_UARTS + 1] = { +#ifdef CONFIG_MN10300_TTYSM0 + [0] = &mn10300_serial_port_sif0, +#endif +#ifdef CONFIG_MN10300_TTYSM1 + [1] = &mn10300_serial_port_sif1, +#endif +#ifdef CONFIG_MN10300_TTYSM2 + [2] = &mn10300_serial_port_sif2, +#endif + [NR_UARTS] = NULL, +}; + + +/* + * we abuse the serial ports' baud timers' interrupt lines to get the ability + * to deliver interrupts to userspace as we use the ports' interrupt lines to + * do virtual DMA on account of the ports having no hardware FIFOs + * + * we can generate an interrupt manually in the assembly stubs by writing to + * the enable and detect bits in the interrupt control register, so all we need + * to do here is disable the interrupt line + * + * note that we can't just leave the line enabled as the baud rate timer *also* + * generates interrupts + */ +static void mn10300_serial_mask_ack(unsigned int irq) +{ + u16 tmp; + GxICR(irq) = GxICR_LEVEL_6; + tmp = GxICR(irq); /* flush write buffer */ +} + +static void mn10300_serial_nop(unsigned int irq) +{ +} + +static struct irq_chip mn10300_serial_pic = { + .name = "mnserial", + .ack = mn10300_serial_mask_ack, + .mask = mn10300_serial_mask_ack, + .mask_ack = mn10300_serial_mask_ack, + .unmask = mn10300_serial_nop, + .end = mn10300_serial_nop, +}; + + +/* + * serial virtual DMA interrupt jump table + */ +struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS]; + +static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->tx_icr = GxICR_LEVEL_1 | GxICR_DETECT; + x = *port->tx_icr; +} + +static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->tx_icr = GxICR_LEVEL_1 | GxICR_ENABLE; + x = *port->tx_icr; +} + +static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->rx_icr = GxICR_LEVEL_1 | GxICR_DETECT; + x = *port->rx_icr; +} + +/* + * multi-bit equivalent of test_and_clear_bit() + */ +static int mask_test_and_clear(volatile u8 *ptr, u8 mask) +{ + u32 epsw; + asm volatile(" bclr %1,(%2) \n" + " mov epsw,%0 \n" + : "=d"(epsw) : "d"(mask), "a"(ptr)); + return !(epsw & EPSW_FLAG_Z); +} + +/* + * receive chars from the ring buffer for this serial port + * - must do break detection here (not done in the UART) + */ +static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) +{ + struct uart_icount *icount = &port->uart.icount; + struct tty_struct *tty = port->uart.info->tty; + unsigned ix; + int count; + u8 st, ch, push, status, overrun; + + _enter("%s", port->name); + + push = 0; + + count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE); + count = tty_buffer_request_room(tty, count); + if (count == 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +try_again: + /* pull chars out of the hat */ + ix = port->rx_outp; + if (ix == port->rx_inp) { + if (push && !tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + + ch = port->rx_buffer[ix++]; + st = port->rx_buffer[ix++]; + smp_rmb(); + port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); + port->uart.icount.rx++; + + st &= SC01STR_FEF | SC01STR_PEF | SC01STR_OEF; + status = 0; + overrun = 0; + + /* the UART doesn't detect BREAK, so we have to do that ourselves + * - it starts as a framing error on a NUL character + * - then we count another two NUL characters before issuing TTY_BREAK + * - then we end on a normal char or one that has all the bottom bits + * zero and the top bits set + */ + switch (port->rx_brk) { + case 0: + /* not breaking at the moment */ + break; + + case 1: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 2; + goto try_again; + } + goto not_break; + + case 2: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 3; + _proto("Rx Break Detected"); + icount->brk++; + if (uart_handle_break(&port->uart)) + goto ignore_char; + status |= 1 << TTY_BREAK; + goto insert; + } + goto not_break; + + default: + if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) + goto try_again; /* still breaking */ + + port->rx_brk = 0; /* end of the break */ + + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + /* discard char at probable break end */ + goto try_again; + } + break; + } + +process_errors: + /* handle framing error */ + if (st & SC01STR_FEF) { + if (ch == 0) { + /* framing error with NUL char is probably a BREAK */ + port->rx_brk = 1; + goto try_again; + } + + _proto("Rx Framing Error"); + icount->frame++; + status |= 1 << TTY_FRAME; + } + + /* handle parity error */ + if (st & SC01STR_PEF) { + _proto("Rx Parity Error"); + icount->parity++; + status = TTY_PARITY; + } + + /* handle normal char */ + if (status == 0) { + if (uart_handle_sysrq_char(&port->uart, ch)) + goto ignore_char; + status = (1 << TTY_NORMAL); + } + + /* handle overrun error */ + if (st & SC01STR_OEF) { + if (port->rx_brk) + goto try_again; + + _proto("Rx Overrun Error"); + icount->overrun++; + overrun = 1; + } + +insert: + status &= port->uart.read_status_mask; + + if (!overrun && !(status & port->uart.ignore_status_mask)) { + int flag; + + if (status & (1 << TTY_BREAK)) + flag = TTY_BREAK; + else if (status & (1 << TTY_PARITY)) + flag = TTY_PARITY; + else if (status & (1 << TTY_FRAME)) + flag = TTY_FRAME; + else + flag = TTY_NORMAL; + + tty_insert_flip_char(tty, ch, flag); + } + + /* overrun is special, since it's reported immediately, and doesn't + * affect the current character + */ + if (overrun) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + count--; + if (count <= 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +ignore_char: + push = 1; + goto try_again; + +not_break: + port->rx_brk = 0; + goto process_errors; +} + +/* + * handle an interrupt from the serial transmission "virtual DMA" driver + * - note: the interrupt routine will disable its own interrupts when the Tx + * buffer is empty + */ +static void mn10300_serial_transmit_interrupt(struct mn10300_serial_port *port) +{ + _enter("%s", port->name); + + if (uart_tx_stopped(&port->uart) || + uart_circ_empty(&port->uart.info->xmit)) + mn10300_serial_dis_tx_intr(port); + + if (uart_circ_chars_pending(&port->uart.info->xmit) < WAKEUP_CHARS) + uart_write_wakeup(&port->uart); +} + +/* + * deal with a change in the status of the CTS line + */ +static void mn10300_serial_cts_changed(struct mn10300_serial_port *port, u8 st) +{ + u16 ctr; + + port->tx_cts = st; + port->uart.icount.cts++; + + /* flip the CTS state selector flag to interrupt when it changes + * back */ + ctr = *port->_control; + ctr ^= SC2CTR_TWS; + *port->_control = ctr; + + uart_handle_cts_change(&port->uart, st & SC2STR_CTS); + wake_up_interruptible(&port->uart.info->delta_msr_wait); +} + +/* + * handle a virtual interrupt generated by the lower level "virtual DMA" + * routines (irq is the baud timer interrupt) + */ +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id) +{ + struct mn10300_serial_port *port = dev_id; + u8 st; + + spin_lock(&port->uart.lock); + + if (port->intr_flags) { + _debug("INT %s: %x", port->name, port->intr_flags); + + if (mask_test_and_clear(&port->intr_flags, MNSCx_RX_AVAIL)) + mn10300_serial_receive_interrupt(port); + + if (mask_test_and_clear(&port->intr_flags, + MNSCx_TX_SPACE | MNSCx_TX_EMPTY)) + mn10300_serial_transmit_interrupt(port); + } + + /* the only modem control line amongst the whole lot is CTS on + * serial port 2 */ + if (port->type == PORT_MN10300_CTS) { + st = *port->_status; + if ((port->tx_cts ^ st) & SC2STR_CTS) + mn10300_serial_cts_changed(port, st); + } + + spin_unlock(&port->uart.lock); + + return IRQ_HANDLED; +} + +/* + * return indication of whether the hardware transmit buffer is empty + */ +static unsigned int mn10300_serial_tx_empty(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + return (*port->_status & (SC01STR_TXF | SC01STR_TBF)) ? + 0 : TIOCSER_TEMT; +} + +/* + * set the modem control lines (we don't have any) + */ +static void mn10300_serial_set_mctrl(struct uart_port *_port, + unsigned int mctrl) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%x", port->name, mctrl); +} + +/* + * get the modem control line statuses + */ +static unsigned int mn10300_serial_get_mctrl(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS && !(*port->_status & SC2STR_CTS)) + return TIOCM_CAR | TIOCM_DSR; + + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +/* + * stop transmitting characters + */ +static void mn10300_serial_stop_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the virtual DMA */ + mn10300_serial_dis_tx_intr(port); +} + +/* + * start transmitting characters + * - jump-start transmission if it has stalled + * - enable the serial Tx interrupt (used by the virtual DMA controller) + * - force an interrupt to happen if necessary + */ +static void mn10300_serial_start_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 x; + + _enter("%s{%lu}", + port->name, + CIRC_CNT(&port->uart.info->xmit.head, + &port->uart.info->xmit.tail, + UART_XMIT_SIZE)); + + /* kick the virtual DMA controller */ + x = *port->tx_icr; + x |= GxICR_ENABLE; + + if (*port->_status & SC01STR_TBF) + x &= ~(GxICR_REQUEST | GxICR_DETECT); + else + x |= GxICR_REQUEST | GxICR_DETECT; + + _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx", + *port->_control, *port->_intr, *port->_status, + *port->_tmxmd, *port->_tmxbr, *port->tx_icr); + + *port->tx_icr = x; + x = *port->tx_icr; +} + +/* + * transmit a high-priority XON/XOFF character + */ +static void mn10300_serial_send_xchar(struct uart_port *_port, char ch) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%02x", port->name, ch); + + if (likely(port->gdbstub)) { + port->tx_xchar = ch; + if (ch) + mn10300_serial_en_tx_intr(port); + } +} + +/* + * stop receiving characters + * - called whilst the port is being closed + */ +static void mn10300_serial_stop_rx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr; + + _enter("%s", port->name); + + ctr = *port->_control; + ctr &= ~SC01CTR_RXE; + *port->_control = ctr; + + mn10300_serial_dis_rx_intr(port); +} + +/* + * enable modem status interrupts + */ +static void mn10300_serial_enable_ms(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr, cts; + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS) { + /* want to interrupt when CTS goes low if CTS is now high and + * vice versa + */ + port->tx_cts = *port->_status; + + cts = (port->tx_cts & SC2STR_CTS) ? + SC2CTR_TWE : SC2CTR_TWE | SC2CTR_TWS; + + ctr = *port->_control; + ctr &= ~SC2CTR_TWS; + ctr |= cts; + *port->_control = ctr; + + mn10300_serial_en_tx_intr(port); + } +} + +/* + * transmit or cease transmitting a break signal + */ +static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%d", port->name, ctl); + + if (ctl) { + /* tell the virtual DMA handler to assert BREAK */ + port->tx_break = 1; + mn10300_serial_en_tx_intr(port); + } else { + port->tx_break = 0; + *port->_control &= ~SC01CTR_BKE; + mn10300_serial_en_tx_intr(port); + } +} + +/* + * grab the interrupts and enable the port for reception + */ +static int mn10300_serial_startup(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + struct mn10300_serial_int *pint; + + _enter("%s{%d}", port->name, port->gdbstub); + + if (unlikely(port->gdbstub)) + return -EBUSY; + + /* allocate an Rx buffer for the virtual DMA handler */ + port->rx_buffer = kmalloc(MNSC_BUFFER_SIZE, GFP_KERNEL); + if (!port->rx_buffer) + return -ENOMEM; + + port->rx_inp = port->rx_outp = 0; + + /* finally, enable the device */ + *port->_intr = SC01ICR_TI; + *port->_control |= SC01CTR_TXE | SC01CTR_RXE; + + pint = &mn10300_serial_int_tbl[port->rx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_rx_handler; + pint = &mn10300_serial_int_tbl[port->tx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_tx_handler; + + set_intr_level(port->rx_irq, GxICR_LEVEL_1); + set_intr_level(port->tx_irq, GxICR_LEVEL_1); + set_irq_chip(port->tm_irq, &mn10300_serial_pic); + + if (request_irq(port->rx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->rx_name, port) < 0) + goto error; + + if (request_irq(port->tx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tx_name, port) < 0) + goto error2; + + if (request_irq(port->tm_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tm_name, port) < 0) + goto error3; + mn10300_serial_mask_ack(port->tm_irq); + + return 0; + +error3: + free_irq(port->tx_irq, port); +error2: + free_irq(port->rx_irq, port); +error: + kfree(port->rx_buffer); + port->rx_buffer = NULL; + return -EBUSY; +} + +/* + * shutdown the port and release interrupts + */ +static void mn10300_serial_shutdown(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the serial port and its baud rate timer */ + port->tx_break = 0; + *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_tmxmd = 0; + + if (port->rx_buffer) { + void *buf = port->rx_buffer; + port->rx_buffer = NULL; + kfree(buf); + } + + /* disable all intrs */ + free_irq(port->tm_irq, port); + free_irq(port->rx_irq, port); + free_irq(port->tx_irq, port); + + *port->rx_icr = GxICR_LEVEL_1; + *port->tx_icr = GxICR_LEVEL_1; +} + +/* + * this routine is called to set the UART divisor registers to match the + * specified baud rate for a serial port. + */ +static void mn10300_serial_change_speed(struct mn10300_serial_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned long flags; + unsigned long ioclk = port->ioclk; + unsigned cflag; + int baud, bits, xdiv, tmp; + u16 tmxbr, scxctr; + u8 tmxmd, battempt; + u8 div_timer = port->div_timer; + + _enter("%s{%lu}", port->name, ioclk); + + /* byte size and parity */ + cflag = new->c_cflag; + switch (cflag & CSIZE) { + case CS7: scxctr = SC01CTR_CLN_7BIT; bits = 9; break; + case CS8: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + default: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + } + + if (cflag & CSTOPB) { + scxctr |= SC01CTR_STB_2BIT; + bits++; + } + + if (cflag & PARENB) { + bits++; + if (cflag & PARODD) + scxctr |= SC01CTR_PB_ODD; +#ifdef CMSPAR + else if (cflag & CMSPAR) + scxctr |= SC01CTR_PB_FIXED0; +#endif + else + scxctr |= SC01CTR_PB_EVEN; + } + + /* Determine divisor based on baud rate */ + battempt = 0; + + if (div_timer == MNSCx_DIV_TIMER_16BIT) + scxctr |= SC0CTR_CK_TM8UFLOW_8; /* ( == SC1CTR_CK_TM9UFLOW_8 + * == SC2CTR_CK_TM10UFLOW) */ + else if (div_timer == MNSCx_DIV_TIMER_8BIT) + scxctr |= SC0CTR_CK_TM2UFLOW_8; + +try_alternative: + baud = uart_get_baud_rate(&port->uart, new, old, 0, + port->ioclk / 8); + + _debug("ALT %d [baud %d]", battempt, baud); + + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + xdiv = 1; + if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + + if (baud == 38400 && + (port->uart.flags & UPF_SPD_MASK) == UPF_SPD_CUST + ) { + _debug("CUSTOM %u", port->uart.custom_divisor); + + if (div_timer == MNSCx_DIV_TIMER_16BIT) { + if (port->uart.custom_divisor <= 65535) { + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + + } else if (div_timer == MNSCx_DIV_TIMER_8BIT) { + if (port->uart.custom_divisor <= 255) { + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + } + } + + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + port->uart.uartclk = ioclk; + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + break; + + case MNSCx_DIV_TIMER_8BIT: + port->uart.uartclk = ioclk; + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + + default: + BUG(); + return; + } + + /* refuse to change to a baud rate we can't support */ + _debug("CAN'T SUPPORT"); + + switch (battempt) { + case 0: + if (old) { + new->c_cflag &= ~CBAUD; + new->c_cflag |= (old->c_cflag & CBAUD); + battempt = 1; + goto try_alternative; + } + + case 1: + /* as a last resort, if the quotient is zero, default to 9600 + * bps */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B9600; + battempt = 2; + goto try_alternative; + + default: + /* hmmm... can't seem to support 9600 either + * - we could try iterating through the speeds we know about to + * find the lowest + */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B0; + + if (div_timer == MNSCx_DIV_TIMER_16BIT) + tmxmd = TM8MD_SRC_IOCLK_32; + else if (div_timer == MNSCx_DIV_TIMER_8BIT) + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = 1; + + port->uart.uartclk = ioclk / 32; + break; + } +timer_okay: + + _debug("UARTCLK: %u / %hu", port->uart.uartclk, tmxbr); + + /* make the changes */ + spin_lock_irqsave(&port->uart.lock, flags); + + uart_update_timeout(&port->uart, new->c_cflag, baud); + + /* set the timer to produce the required baud rate */ + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxbr = tmxbr; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *(volatile u8 *) port->_tmxbr = (u8) tmxbr; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* CTS flow control flag and modem status interrupts */ + scxctr &= ~(SC2CTR_TWE | SC2CTR_TWS); + + if (port->type == PORT_MN10300_CTS && cflag & CRTSCTS) { + /* want to interrupt when CTS goes low if CTS is now + * high and vice versa + */ + port->tx_cts = *port->_status; + + if (port->tx_cts & SC2STR_CTS) + scxctr |= SC2CTR_TWE; + else + scxctr |= SC2CTR_TWE | SC2CTR_TWS; + } + + /* set up parity check flag */ + port->uart.read_status_mask = (1 << TTY_NORMAL) | (1 << TTY_OVERRUN); + if (new->c_iflag & INPCK) + port->uart.read_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & (BRKINT | PARMRK)) + port->uart.read_status_mask |= (1 << TTY_BREAK); + + /* characters to ignore */ + port->uart.ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & IGNBRK) { + port->uart.ignore_status_mask |= (1 << TTY_BREAK); + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= (1 << TTY_OVERRUN); + } + + /* Ignore all characters if CREAD is not set */ + if ((new->c_cflag & CREAD) == 0) + port->uart.ignore_status_mask |= (1 << TTY_NORMAL); + + scxctr |= *port->_control & (SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_control = scxctr; + + spin_unlock_irqrestore(&port->uart.lock, flags); +} + +/* + * set the terminal I/O parameters + */ +static void mn10300_serial_set_termios(struct uart_port *_port, + struct ktermios *new, + struct ktermios *old) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%p,%p", port->name, new, old); + + mn10300_serial_change_speed(port, new, old); + + /* handle turning off CRTSCTS */ + if (!(new->c_cflag & CRTSCTS)) { + u16 ctr = *port->_control; + ctr &= ~SC2CTR_TWE; + *port->_control = ctr; + } +} + +/* + * return description of port type + */ +static const char *mn10300_serial_type(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + if (port->uart.type == PORT_MN10300_CTS) + return "MN10300 SIF_CTS"; + + return "MN10300 SIF"; +} + +/* + * release I/O and memory regions in use by port + */ +static void mn10300_serial_release_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + release_mem_region((unsigned long) port->_iobase, 16); +} + +/* + * request I/O and memory regions for port + */ +static int mn10300_serial_request_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + request_mem_region((unsigned long) port->_iobase, 16, port->name); + return 0; +} + +/* + * configure the type and reserve the ports + */ +static void mn10300_serial_config_port(struct uart_port *_port, int type) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + port->uart.type = PORT_MN10300; + + if (port->options & MNSCx_OPT_CTS) + port->uart.type = PORT_MN10300_CTS; + + mn10300_serial_request_port(_port); +} + +/* + * verify serial parameters are suitable for this port type + */ +static int mn10300_serial_verify_port(struct uart_port *_port, + struct serial_struct *ss) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + void *mapbase = (void *) (unsigned long) port->uart.mapbase; + + _enter("%s", port->name); + + /* these things may not be changed */ + if (ss->irq != port->uart.irq || + ss->port != port->uart.iobase || + ss->io_type != port->uart.iotype || + ss->iomem_base != mapbase || + ss->iomem_reg_shift != port->uart.regshift || + ss->hub6 != port->uart.hub6 || + ss->xmit_fifo_size != port->uart.fifosize) + return -EINVAL; + + /* type may be changed on a port that supports CTS */ + if (ss->type != port->uart.type) { + if (!(port->options & MNSCx_OPT_CTS)) + return -EINVAL; + + if (ss->type != PORT_MN10300 && + ss->type != PORT_MN10300_CTS) + return -EINVAL; + } + + return 0; +} + +/* + * initialise the MN10300 on-chip UARTs + */ +static int __init mn10300_serial_init(void) +{ + struct mn10300_serial_port *port; + int ret, i; + + printk(KERN_INFO "%s version %s (%s)\n", + serial_name, serial_version, serial_revdate); + +#ifdef CONFIG_MN10300_TTYSM2 + SC2TIM = 8; /* make the baud base of timer 2 IOCLK/8 */ +#endif + + set_intr_stub(EXCEP_IRQ_LEVEL1, mn10300_serial_vdma_interrupt); + + ret = uart_register_driver(&mn10300_serial_driver); + if (!ret) { + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (!port || port->gdbstub) + continue; + + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + ret = uart_add_one_port(&mn10300_serial_driver, + &port->uart); + + if (ret < 0) { + _debug("ERROR %d", -ret); + break; + } + } + + if (ret) + uart_unregister_driver(&mn10300_serial_driver); + } + + return ret; +} + +__initcall(mn10300_serial_init); + + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + +/* + * print a string to the serial port without disturbing the real user of the + * port too much + * - the console must be locked by the caller + */ +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count) +{ + struct mn10300_serial_port *port; + unsigned i; + u16 scxctr, txicr, tmp; + u8 tmxmd; + + port = mn10300_serial_ports[co->index]; + + /* firstly hijack the serial port from the "virtual DMA" controller */ + txicr = *port->tx_icr; + *port->tx_icr = GxICR_LEVEL_1; + tmp = *port->tx_icr; + + /* the transmitter may be disabled */ + scxctr = *port->_control; + if (!(scxctr & SC01CTR_TXE)) { + /* restart the UART clock */ + tmxmd = *port->_tmxmd; + + switch (port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* enable the transmitter */ + *port->_control = (scxctr & ~SC01CTR_BKE) | SC01CTR_TXE; + + } else if (scxctr & SC01CTR_BKE) { + /* stop transmitting BREAK */ + *port->_control = (scxctr & ~SC01CTR_BKE); + } + + /* send the chars into the serial port (with LF -> LFCR conversion) */ + for (i = 0; i < count; i++) { + char ch = *s++; + + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = ch; + + if (ch == 0x0a) { + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = 0xd; + } + } + + /* can't let the transmitter be turned off if it's actually + * transmitting */ + while (*port->_status & (SC01STR_TXF | SC01STR_TBF)) + continue; + + /* disable the transmitter if we re-enabled it */ + if (!(scxctr & SC01CTR_TXE)) + *port->_control = scxctr; + + *port->tx_icr = txicr; + tmp = *port->tx_icr; +} + +/* + * set up a serial port as a console + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * - return non-zero if we didn't find a serial port. + */ +static int __init mn10300_serial_console_setup(struct console *co, + char *options) +{ + struct mn10300_serial_port *port; + int i, parity = 'n', baud = 9600, bits = 8, flow = 0; + + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (port && !port->gdbstub && port->uart.line == co->index) + goto found_device; + } + + return -ENODEV; + +found_device: + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&port->uart, co, baud, parity, bits, flow); +} + +/* + * register console + */ +static int __init mn10300_serial_console_init(void) +{ + register_console(&mn10300_serial_console); + return 0; +} + +console_initcall(mn10300_serial_console_init); +#endif diff --git a/arch/mn10300/kernel/mn10300-serial.h b/arch/mn10300/kernel/mn10300-serial.h new file mode 100644 index 00000000000..6796499bf78 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.h @@ -0,0 +1,126 @@ +/* MN10300 On-chip serial port driver definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _MN10300_SERIAL_H +#define _MN10300_SERIAL_H + +#ifndef __ASSEMBLY__ +#include <linux/serial_core.h> +#include <linux/termios.h> +#endif + +#include <asm/page.h> +#include <asm/serial-regs.h> + +#define NR_PORTS 3 /* should be set 3 or 9 or 16 */ + +#define MNSC_BUFFER_SIZE +(PAGE_SIZE / 2) + +/* intr_flags bits */ +#define MNSCx_RX_AVAIL 0x01 +#define MNSCx_RX_OVERF 0x02 +#define MNSCx_TX_SPACE 0x04 +#define MNSCx_TX_EMPTY 0x08 + +#ifndef __ASSEMBLY__ + +struct mn10300_serial_port { + char *rx_buffer; /* reception buffer base */ + unsigned rx_inp; /* pointer to rx input offset */ + unsigned rx_outp; /* pointer to rx output offset */ + u8 tx_xchar; /* high-priority XON/XOFF buffer */ + u8 tx_break; /* transmit break request */ + u8 intr_flags; /* interrupt flags */ + volatile u16 *rx_icr; /* Rx interrupt control register */ + volatile u16 *tx_icr; /* Tx interrupt control register */ + int rx_irq; /* reception IRQ */ + int tx_irq; /* transmission IRQ */ + int tm_irq; /* timer IRQ */ + + const char *name; /* name of serial port */ + const char *rx_name; /* Rx interrupt handler name of serial port */ + const char *tx_name; /* Tx interrupt handler name of serial port */ + const char *tm_name; /* Timer interrupt handler name */ + unsigned short type; /* type of serial port */ + unsigned char isconsole; /* T if it's a console */ + volatile void *_iobase; /* pointer to base of I/O control regs */ + volatile u16 *_control; /* control register pointer */ + volatile u8 *_status; /* status register pointer */ + volatile u8 *_intr; /* interrupt register pointer */ + volatile void *_rxb; /* receive buffer register pointer */ + volatile void *_txb; /* transmit buffer register pointer */ + volatile u16 *_tmicr; /* timer interrupt control register */ + volatile u8 *_tmxmd; /* baud rate timer mode register */ + volatile u16 *_tmxbr; /* baud rate timer base register */ + + /* this must come down here so that assembly can use BSET to access the + * above fields */ + struct uart_port uart; + + unsigned short rx_brk; /* current break reception status */ + u16 tx_cts; /* current CTS status */ + int gdbstub; /* preemptively stolen by GDB stub */ + + u8 clock_src; /* clock source */ +#define MNSCx_CLOCK_SRC_IOCLK 0 +#define MNSCx_CLOCK_SRC_IOBCLK 1 + + u8 div_timer; /* timer used as divisor */ +#define MNSCx_DIV_TIMER_16BIT 0 +#define MNSCx_DIV_TIMER_8BIT 1 + + u16 options; /* options */ +#define MNSCx_OPT_CTS 0x0001 + + unsigned long ioclk; /* base clock rate */ +}; + +#ifdef CONFIG_MN10300_TTYSM0 +extern struct mn10300_serial_port mn10300_serial_port_sif0; +#endif + +#ifdef CONFIG_MN10300_TTYSM1 +extern struct mn10300_serial_port mn10300_serial_port_sif1; +#endif + +#ifdef CONFIG_MN10300_TTYSM2 +extern struct mn10300_serial_port mn10300_serial_port_sif2; +#endif + +extern struct mn10300_serial_port *mn10300_serial_ports[]; + +struct mn10300_serial_int { + struct mn10300_serial_port *port; + asmlinkage void (*vdma)(void); +}; + +extern struct mn10300_serial_int mn10300_serial_int_tbl[]; + +extern asmlinkage void mn10300_serial_vdma_interrupt(void); +extern asmlinkage void mn10300_serial_vdma_rx_handler(void); +extern asmlinkage void mn10300_serial_vdma_tx_handler(void); + +#endif /* __ASSEMBLY__ */ + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +#define SCgSTR SC0STR +#define SCgRXB SC0RXB +#define SCgRXIRQ SC0RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +#define SCgSTR SC1STR +#define SCgRXB SC1RXB +#define SCgRXIRQ SC1RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM2) +#define SCgSTR SC2STR +#define SCgRXB SC2RXB +#define SCgRXIRQ SC2RXIRQ +#endif + +#endif /* _MN10300_SERIAL_H */ diff --git a/arch/mn10300/kernel/mn10300-watchdog-low.S b/arch/mn10300/kernel/mn10300-watchdog-low.S new file mode 100644 index 00000000000..996244745cc --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog-low.S @@ -0,0 +1,59 @@ +############################################################################### +# +# MN10300 Watchdog interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> +#include <asm/frame.inc> + + .text + +############################################################################### +# +# Watchdog handler entry point +# - special non-maskable interrupt +# +############################################################################### + .globl watchdog_handler + .type watchdog_handler,@function +watchdog_handler: + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + lsr 2,d1 + call watchdog_interrupt[],0 # watchdog_interrupt(regs,irq) + + jmp ret_from_intr + + .size watchdog_handler,.-watchdog_handler + +############################################################################### +# +# Watchdog touch entry point +# - kept to absolute minimum (unfortunately, it's prototyped in linux/nmi.h so +# we can't inline it) +# +############################################################################### + .globl touch_nmi_watchdog + .type touch_nmi_watchdog,@function +touch_nmi_watchdog: + clr d0 + mov d0,(watchdog_alert_counter) + ret [],0 + + .size touch_nmi_watchdog,.-touch_nmi_watchdog diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c new file mode 100644 index 00000000000..10811e981d2 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog.c @@ -0,0 +1,183 @@ +/* MN10300 Watchdog timer + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/nmi.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/nmi.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/atomic.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <asm/div64.h> +#include <asm/smp.h> +#include <asm/gdb-stub.h> +#include <asm/proc/clock.h> + +static DEFINE_SPINLOCK(watchdog_print_lock); +static unsigned int watchdog; +static unsigned int watchdog_hz = 1; +unsigned int watchdog_alert_counter; + +EXPORT_SYMBOL(touch_nmi_watchdog); + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check its timer makes IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ +static unsigned int last_irq_sums[NR_CPUS]; + +int __init check_watchdog(void) +{ + irq_cpustat_t tmp[1]; + + printk(KERN_INFO "Testing Watchdog... "); + + memcpy(tmp, irq_stat, sizeof(tmp)); + local_irq_enable(); + mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */ + local_irq_disable(); + + if (nmi_count(0) - tmp[0].__nmi_count <= 5) { + printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n", + 0); + return -1; + } + + printk(KERN_INFO "OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + * something more reasonable; makes a difference in some configs + */ + watchdog_hz = 1; + + return 0; +} + +static int __init setup_watchdog(char *str) +{ + unsigned tmp; + int opt; + u8 ctr; + + get_option(&str, &opt); + if (opt != 1) + return 0; + + watchdog = opt; + if (watchdog) { + set_intr_stub(EXCEP_WDT, watchdog_handler); + ctr = WDCTR_WDCK_65536th; + WDCTR = WDCTR_WDRST | ctr; + WDCTR = ctr; + tmp = WDCTR; + + tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK); + tmp = 1000000000 / tmp; + watchdog_hz = (tmp + 500) / 1000; + } + + return 1; +} + +__setup("watchdog=", setup_watchdog); + +void __init watchdog_go(void) +{ + u8 wdt; + + if (watchdog) { + printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz); + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt | WDCTR_WDRST; + wdt = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + wdt = WDCTR; + + check_watchdog(); + } +} + +asmlinkage +void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) +{ + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + u8 wdt, tmp; + + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt; + tmp = WDCTR; + NMICR = NMICR_WDIF; + + nmi_count(cpu)++; + kstat_this_cpu.irqs[NMIIRQ]++; + sum = irq_stat[cpu].__irq_count; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + watchdog_alert_counter++; + if (watchdog_alert_counter == 5 * watchdog_hz) { + spin_lock(&watchdog_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk(KERN_ERR + "NMI Watchdog detected LOCKUP on CPU%d," + " pc %08lx, registers:\n", + cpu, regs->pc); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&watchdog_print_lock); + bust_spinlocks(0); +#ifdef CONFIG_GDBSTUB + if (gdbstub_busy) + gdbstub_exception(regs, excep); + else + gdbstub_intercept(regs, excep); +#endif + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + watchdog_alert_counter = 0; + } + + WDCTR = wdt | WDCTR_WDRST; + tmp = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + tmp = WDCTR; +} diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c new file mode 100644 index 00000000000..6d19628634e --- /dev/null +++ b/arch/mn10300/kernel/mn10300_ksyms.c @@ -0,0 +1,37 @@ +/* MN10300 Miscellaneous and library kernel exports + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/uaccess.h> + + +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_change_bit); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memset); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +extern u64 __ashrdi3(u64, unsigned); +extern u64 __ashldi3(u64, unsigned); +extern u64 __lshrdi3(u64, unsigned); +extern s64 __negdi2(s64); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__negdi2); diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c new file mode 100644 index 00000000000..0e4d2f6fa6e --- /dev/null +++ b/arch/mn10300/kernel/module.c @@ -0,0 +1,206 @@ +/* MN10300 Kernel module helper routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * - Derived from arch/i386/kernel/module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * 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 Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt, ...) +#endif + +/* + * allocate storage for a module + */ +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc_exec(size); +} + +/* + * free memory returned from module_alloc() + */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + * table entries. */ +} + +/* + * allow the arch to fix up the section table + * - we don't need anything special + */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +static uint32_t reloc_get16(uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static uint32_t reloc_get24(uint8_t *p) +{ + return reloc_get16(p) | (p[2] << 16); +} + +static uint32_t reloc_get32(uint8_t *p) +{ + return reloc_get16(p) | (reloc_get16(p+2) << 16); +} + +static void reloc_put16(uint8_t *p, uint32_t val) +{ + p[0] = val & 0xff; + p[1] = (val >> 8) & 0xff; +} + +static void reloc_put24(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + p[2] = (val >> 16) & 0xff; +} + +static void reloc_put32(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + reloc_put16(p+2, val >> 16); +} + +/* + * apply a REL relocation + */ +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +/* + * apply a RELA relocation + */ +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + Elf32_Addr relocation; + uint8_t *location; + uint32_t value; + + DEBUGP("Applying relocate section %u to %u\n", + relsec, sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* this is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + /* this is the symbol the relocation is referring to (note that + * all undefined symbols have been resolved by the caller) */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + /* this is the adjustment to be made */ + relocation = sym->st_value + rel[i].r_addend; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + /* for the first four relocation types, we add the + * adjustment into the value at the location given */ + case R_MN10300_32: + value = reloc_get32(location); + value += relocation; + reloc_put32(location, value); + break; + case R_MN10300_24: + value = reloc_get24(location); + value += relocation; + reloc_put24(location, value); + break; + case R_MN10300_16: + value = reloc_get16(location); + value += relocation; + reloc_put16(location, value); + break; + case R_MN10300_8: + *location += relocation; + break; + + /* for the next three relocation types, we write the + * adjustment with the address subtracted over the + * value at the location given */ + case R_MN10300_PCREL32: + value = relocation - (uint32_t) location; + reloc_put32(location, value); + break; + case R_MN10300_PCREL16: + value = relocation - (uint32_t) location; + reloc_put16(location, value); + break; + case R_MN10300_PCREL8: + *location = relocation - (uint32_t) location; + break; + + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +/* + * finish loading the module + */ +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +/* + * finish clearing the module + */ +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c new file mode 100644 index 00000000000..3b0d579fc15 --- /dev/null +++ b/arch/mn10300/kernel/process.c @@ -0,0 +1,297 @@ +/* MN10300 Process handling code + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/percpu.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu_context.h> +#include <asm/fpu.h> +#include <asm/reset-regs.h> +#include <asm/gdb-stub.h> +#include "internal.h" + +/* + * power management idle function, if any.. + */ +void (*pm_idle)(void); +EXPORT_SYMBOL(pm_idle); + +/* + * return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return ((unsigned long *) tsk->thread.sp)[3]; +} + +/* + * power off function, if any + */ +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +/* + * we use this if we don't have any better idle routine + */ +static void default_idle(void) +{ + local_irq_disable(); + if (!need_resched()) + safe_halt(); + else + local_irq_enable(); +} + +/* + * the idle thread + * - there's no useful work to be done, so just try to conserve power and have + * a low exit latency (ie sit in a loop waiting for somebody to say that + * they'd like to reschedule) + */ +void cpu_idle(void) +{ + int cpu = smp_processor_id(); + + /* endless idle loop with no priority at all */ + for (;;) { + while (!need_resched()) { + void (*idle)(void); + + smp_rmb(); + idle = pm_idle; + if (!idle) + idle = default_idle; + + irq_stat[cpu].idle_timestamp = jiffies; + idle(); + } + + preempt_enable_no_resched(); + schedule(); + preempt_disable(); + } +} + +void release_segments(struct mm_struct *mm) +{ +} + +void machine_restart(char *cmd) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif + +#ifdef mn10300_unit_hard_reset + mn10300_unit_hard_reset(); +#else + mn10300_proc_hard_reset(); +#endif +} + +void machine_halt(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif +} + +void machine_power_off(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif +} + +void show_regs(struct pt_regs *regs) +{ +} + +/* + * create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.a2 = (unsigned long) fn; + regs.d2 = (unsigned long) arg; + regs.pc = (unsigned long) kernel_thread_helper; + local_save_flags(regs.epsw); + regs.epsw |= EPSW_IE | EPSW_IM_7; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, + NULL, NULL); +} + +/* + * free current thread data structures etc.. + */ +void exit_thread(void) +{ + exit_fpu(); +} + +void flush_thread(void) +{ + flush_fpu(); +} + +void release_thread(struct task_struct *dead_task) +{ +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ +} + +/* + * this gets called before we allocate a new thread and copy the current task + * into it so that we can store lazy state into memory + */ +void prepare_to_copy(struct task_struct *tsk) +{ + unlazy_fpu(tsk); +} + +/* + * set up the kernel stack for a new thread and copy arch-specific thread + * control information + */ +int copy_thread(int nr, unsigned long clone_flags, + unsigned long c_usp, unsigned long ustk_size, + struct task_struct *p, struct pt_regs *kregs) +{ + struct pt_regs *c_uregs, *c_kregs, *uregs; + unsigned long c_ksp; + + uregs = current->thread.uregs; + + c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE; + + /* allocate the userspace exception frame and set it up */ + c_ksp -= sizeof(struct pt_regs); + c_uregs = (struct pt_regs *) c_ksp; + + p->thread.uregs = c_uregs; + *c_uregs = *uregs; + c_uregs->sp = c_usp; + c_uregs->epsw &= ~EPSW_FE; /* my FPU */ + + c_ksp -= 12; /* allocate function call ABI slack */ + + /* the new TLS pointer is passed in as arg #5 to sys_clone() */ + if (clone_flags & CLONE_SETTLS) + c_uregs->e2 = __frame->d3; + + /* set up the return kernel frame if called from kernel_thread() */ + c_kregs = c_uregs; + if (kregs != uregs) { + c_ksp -= sizeof(struct pt_regs); + c_kregs = (struct pt_regs *) c_ksp; + *c_kregs = *kregs; + c_kregs->sp = c_usp; + c_kregs->next = c_uregs; +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + c_kregs->e2 = (unsigned long) p; /* current */ +#endif + + c_ksp -= 12; /* allocate function call ABI slack */ + } + + /* set up things up so the scheduler can start the new task */ + p->thread.__frame = c_kregs; + p->thread.a3 = (unsigned long) c_kregs; + p->thread.sp = c_ksp; + p->thread.pc = (unsigned long) ret_from_fork; + p->thread.wchan = (unsigned long) ret_from_fork; + p->thread.usp = c_usp; + + return 0; +} + +/* + * clone a process + * - tlsptr is retrieved by copy_thread() from __frame->d3 + */ +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, + int __user *parent_tidptr, int __user *child_tidptr, + int __user *tlsptr) +{ + return do_fork(clone_flags, newsp ?: __frame->sp, __frame, 0, + parent_tidptr, child_tidptr); +} + +asmlinkage long sys_fork(void) +{ + return do_fork(SIGCHLD, __frame->sp, __frame, 0, NULL, NULL); +} + +asmlinkage long sys_vfork(void) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, __frame->sp, __frame, + 0, NULL, NULL); +} + +asmlinkage long sys_execve(char __user *name, + char __user * __user *argv, + char __user * __user *envp) +{ + char *filename; + int error; + + lock_kernel(); + + filename = getname(name); + error = PTR_ERR(filename); + if (!IS_ERR(filename)) { + error = do_execve(filename, argv, envp, __frame); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + + putname(filename); + } + + unlock_kernel(); + return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ + return p->thread.wchan; +} diff --git a/arch/mn10300/kernel/profile-low.S b/arch/mn10300/kernel/profile-low.S new file mode 100644 index 00000000000..94ffac12d02 --- /dev/null +++ b/arch/mn10300/kernel/profile-low.S @@ -0,0 +1,72 @@ +############################################################################### +# +# Fast profiling interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> + +#define pi break + + .balign 4 +counter: + .long -1 + +############################################################################### +# +# Profiling interrupt entry point +# - intended to run at interrupt priority 1 +# +############################################################################### +ENTRY(profile_handler) + movm [d2,d3,a2],(sp) + + # ignore userspace + mov (12,sp),d2 + and EPSW_nSL,d2 + bne out + + # do nothing if there's no buffer + mov (prof_buffer),a2 + and a2,a2 + beq out + or 0x20000000,a2 + + # calculate relative position in text segment + mov (16,sp),d2 + sub _stext,d2 + mov (prof_shift),d3 + lsr d3,d2 + mov (prof_len),d3 + cmp d3,d2 + bcc outside_text + + # increment the appropriate profile bucket +do_inc: + asl2 d2 + mov (a2,d2),d3 + inc d3 + mov d3,(a2,d2) +out: + mov GxICR_DETECT,d2 + movbu d2,(TM11ICR) # ACK the interrupt + movbu (TM11ICR),d2 + movm (sp),[d2,d3,a2] + rti + +outside_text: + sub 1,d3 + mov d3,d2 + bra do_inc diff --git a/arch/mn10300/kernel/profile.c b/arch/mn10300/kernel/profile.c new file mode 100644 index 00000000000..20d7d0306b1 --- /dev/null +++ b/arch/mn10300/kernel/profile.c @@ -0,0 +1,51 @@ +/* MN10300 Profiling setup + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * initialise the profiling if enabled + * - using with gdbstub will give anomalous results + * - can't be used with gdbstub if running at IRQ priority 0 + */ +static __init int profile_init(void) +{ + u16 tmp; + + if (!prof_buffer) + return 0; + + /* use timer 11 to drive the profiling interrupts */ + set_intr_stub(EXCEP_IRQ_LEVEL0, profile_handler); + + /* set IRQ priority at which to run */ + set_intr_level(TM11IRQ, GxICR_LEVEL_0); + + /* set up timer 11 + * - source: (IOCLK 33MHz)*2 = 66MHz + * - frequency: (33330000*2) / 8 / 20625 = 202Hz + */ + TM11BR = 20625 - 1; + TM11MD = TM8MD_SRC_IOCLK_8; + TM11MD |= TM8MD_INIT_COUNTER; + TM11MD &= ~TM8MD_INIT_COUNTER; + TM11MD |= TM8MD_COUNT_ENABLE; + + TM11ICR |= GxICR_ENABLE; + tmp = TM11ICR; + + printk(KERN_INFO "Profiling initiated on timer 11, priority 0, %uHz\n", + mn10300_ioclk / 8 / (TM11BR + 1)); + printk(KERN_INFO "Profile histogram stored %p-%p\n", + prof_buffer, (u8 *)(prof_buffer + prof_len) - 1); + + return 0; +} + +__initcall(profile_init); diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c new file mode 100644 index 00000000000..d6d6cdc75c5 --- /dev/null +++ b/arch/mn10300/kernel/ptrace.c @@ -0,0 +1,379 @@ +/* MN10300 Process tracing + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/fpu.h> +#include <asm/asm-offsets.h> + +/* + * translate ptrace register IDs into struct pt_regs offsets + */ +static const u8 ptrace_regid_to_frame[] = { + [PT_A3 << 2] = REG_A3, + [PT_A2 << 2] = REG_A2, + [PT_D3 << 2] = REG_D3, + [PT_D2 << 2] = REG_D2, + [PT_MCVF << 2] = REG_MCVF, + [PT_MCRL << 2] = REG_MCRL, + [PT_MCRH << 2] = REG_MCRH, + [PT_MDRQ << 2] = REG_MDRQ, + [PT_E1 << 2] = REG_E1, + [PT_E0 << 2] = REG_E0, + [PT_E7 << 2] = REG_E7, + [PT_E6 << 2] = REG_E6, + [PT_E5 << 2] = REG_E5, + [PT_E4 << 2] = REG_E4, + [PT_E3 << 2] = REG_E3, + [PT_E2 << 2] = REG_E2, + [PT_SP << 2] = REG_SP, + [PT_LAR << 2] = REG_LAR, + [PT_LIR << 2] = REG_LIR, + [PT_MDR << 2] = REG_MDR, + [PT_A1 << 2] = REG_A1, + [PT_A0 << 2] = REG_A0, + [PT_D1 << 2] = REG_D1, + [PT_D0 << 2] = REG_D0, + [PT_ORIG_D0 << 2] = REG_ORIG_D0, + [PT_EPSW << 2] = REG_EPSW, + [PT_PC << 2] = REG_PC, +}; + +static inline int get_stack_long(struct task_struct *task, int offset) +{ + return *(unsigned long *) + ((unsigned long) task->thread.uregs + offset); +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline +int put_stack_long(struct task_struct *task, int offset, unsigned long data) +{ + unsigned long stack; + + stack = (unsigned long) task->thread.uregs + offset; + *(unsigned long *) stack = data; + return 0; +} + +static inline unsigned long get_fpregs(struct fpu_state_struct *buf, + struct task_struct *tsk) +{ + return __copy_to_user(buf, &tsk->thread.fpu_state, + sizeof(struct fpu_state_struct)); +} + +static inline unsigned long set_fpregs(struct task_struct *tsk, + struct fpu_state_struct *buf) +{ + return __copy_from_user(&tsk->thread.fpu_state, buf, + sizeof(struct fpu_state_struct)); +} + +static inline void fpsave_init(struct task_struct *task) +{ + memset(&task->thread.fpu_state, 0, sizeof(struct fpu_state_struct)); +} + +/* + * make sure the single step bit is not set + */ +void ptrace_disable(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp &= ~EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +/* + * set the single step bit + */ +void ptrace_enable(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp |= EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +/* + * handle the arch-specific side of process tracing + */ +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + struct fpu_state_struct fpu_state; + int i, ret; + + switch (request) { + /* read the word at location addr. */ + case PTRACE_PEEKTEXT: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* read the word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + if (addr < NR_PTREGS << 2) + tmp = get_stack_long(child, + ptrace_regid_to_frame[addr]); + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* write the word at location addr. */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + if (access_process_vm(child, addr, &data, sizeof(data), 1) == + sizeof(data)) + ret = 0; + else + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + ret = 0; + if (addr < NR_PTREGS << 2) + ret = put_stack_long(child, ptrace_regid_to_frame[addr], + data); + break; + + /* continue and stop at next (return from) syscall */ + case PTRACE_SYSCALL: + /* restart after signal. */ + case PTRACE_CONT: + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + ptrace_disable(child); + wake_up_process(child); + ret = 0; + break; + + /* + * make the child exit + * - the best I can do is send it a sigkill + * - perhaps it should be put in the status that it wants to + * exit + */ + case PTRACE_KILL: + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_tsk_thread_flag(child, TIF_SINGLESTEP); + ptrace_disable(child); + wake_up_process(child); + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ +#ifndef CONFIG_MN10300_USING_JTAG + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + ptrace_enable(child); + child->exit_code = data; + wake_up_process(child); + ret = 0; +#else + ret = -EINVAL; +#endif + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + /* Get all gp regs from the child. */ + case PTRACE_GETREGS: { + unsigned long tmp; + + if (!access_ok(VERIFY_WRITE, (unsigned *) data, NR_PTREGS << 2)) { + ret = -EIO; + break; + } + + for (i = 0; i < NR_PTREGS << 2; i += 4) { + tmp = get_stack_long(child, ptrace_regid_to_frame[i]); + __put_user(tmp, (unsigned long *) data); + data += sizeof(tmp); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + + if (!access_ok(VERIFY_READ, (unsigned long *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + + for (i = 0; i < NR_PTREGS << 2; i += 4) { + __get_user(tmp, (unsigned long *) data); + put_stack_long(child, ptrace_regid_to_frame[i], tmp); + data += sizeof(tmp); + } + ret = 0; + break; + } + + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + if (is_using_fpu(child)) { + unlazy_fpu(child); + fpu_state = child->thread.fpu_state; + } else { + memset(&fpu_state, 0, sizeof(fpu_state)); + } + + ret = -EIO; + if (copy_to_user((void *) data, &fpu_state, + sizeof(fpu_state)) == 0) + ret = 0; + break; + } + + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = -EFAULT; + if (copy_from_user(&fpu_state, (const void *) data, + sizeof(fpu_state)) == 0) { + fpu_kill_state(child); + child->thread.fpu_state = fpu_state; + set_using_fpu(child); + ret = 0; + } + break; + } + + case PTRACE_SETOPTIONS: { + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } + + return ret; +} + +/* + * notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) +{ +#if 0 + /* just in case... */ + printk(KERN_DEBUG "[%d] syscall_%lu(%lx,%lx,%lx,%lx) = %lx\n", + current->pid, + regs->orig_d0, + regs->a0, + regs->d1, + regs->a3, + regs->a2, + regs->d0); + return; +#endif + + if (!test_thread_flag(TIF_SYSCALL_TRACE) && + !test_thread_flag(TIF_SINGLESTEP)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | + ((current->ptrace & PT_TRACESYSGOOD) && + !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/mn10300/kernel/rtc.c b/arch/mn10300/kernel/rtc.c new file mode 100644 index 00000000000..042f792d843 --- /dev/null +++ b/arch/mn10300/kernel/rtc.c @@ -0,0 +1,173 @@ +/* MN10300 RTC management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mc146818rtc.h> +#include <linux/bcd.h> +#include <linux/timex.h> +#include <asm/rtc-regs.h> +#include <asm/rtc.h> + +DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL(rtc_lock); + +/* last time the RTC got updated */ +static long last_rtc_update; + +/* time for RTC to update itself in ioclks */ +static unsigned long mn10300_rtc_update_period; + +/* + * read the current RTC time + */ +unsigned long __init get_initial_rtc_time(void) +{ + struct rtc_time tm; + + get_rtc_time(&tm); + + return mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500 + * ms after the second nowtime has started, because when nowtime is written + * into the registers of the CMOS clock, it will jump to the next second + * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data + * sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being + * set */ + CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset + * prescaler */ + CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) + /* correct for half hour time zone */ + real_minutes += 30; + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds, RTC_SECONDS); + CMOS_WRITE(real_minutes, RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +void check_rtc_time(void) +{ + /* the RTC clock just finished ticking over again this second + * - if we have an externally synchronized Linux clock, then update + * RTC clock accordingly every ~11 minutes. set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_nsec / 1000 >= 500000 - ((unsigned) TICK_SIZE) / 2 && + xtime.tv_nsec / 1000 <= 500000 + ((unsigned) TICK_SIZE) / 2 + ) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + /* do it again in 60s */ + last_rtc_update = xtime.tv_sec - 600; + } +} + +/* + * calibrate the TSC clock against the RTC + */ +void __init calibrate_clock(void) +{ + unsigned long count0, counth, count1; + unsigned char status; + + /* make sure the RTC is running and is set to operate in 24hr mode */ + status = RTSRC; + RTCRB |= RTCRB_SET; + RTCRB |= RTCRB_TM_24HR; + RTCRA |= RTCRA_DVR; + RTCRA &= ~RTCRA_DVR; + RTCRB &= ~RTCRB_SET; + + /* work out the clock speed by counting clock cycles between ends of + * the RTC update cycle - track the RTC through one complete update + * cycle (1 second) + */ + startup_timestamp_counter(); + + while (!(RTCRA & RTCRA_UIP)) {} + while ((RTCRA & RTCRA_UIP)) {} + + count0 = TMTSCBC; + + while (!(RTCRA & RTCRA_UIP)) {} + + counth = TMTSCBC; + + while ((RTCRA & RTCRA_UIP)) {} + + count1 = TMTSCBC; + + shutdown_timestamp_counter(); + + MN10300_TSCCLK = count0 - count1; /* the timers count down */ + mn10300_rtc_update_period = counth - count1; + MN10300_TSC_PER_HZ = MN10300_TSCCLK / HZ; +} diff --git a/arch/mn10300/kernel/semaphore.c b/arch/mn10300/kernel/semaphore.c new file mode 100644 index 00000000000..9153c4039fd --- /dev/null +++ b/arch/mn10300/kernel/semaphore.c @@ -0,0 +1,149 @@ +/* MN10300 Semaphore implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/module.h> +#include <asm/semaphore.h> + +struct sem_waiter { + struct list_head list; + struct task_struct *task; +}; + +#if SEMAPHORE_DEBUG +void semtrace(struct semaphore *sem, const char *str) +{ + if (sem->debug) + printk(KERN_DEBUG "[%d] %s({%d,%d})\n", + current->pid, + str, + atomic_read(&sem->count), + list_empty(&sem->wait_list) ? 0 : 1); +} +#else +#define semtrace(SEM, STR) do { } while (0) +#endif + +/* + * wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +void __down(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + + semtrace(sem, "Entering __down"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + for (;;) { + if (!waiter.task) + break; + schedule(); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + } + + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down"); +} +EXPORT_SYMBOL(__down); + +/* + * interruptibly wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +int __down_interruptible(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + int ret; + + semtrace(sem, "Entering __down_interruptible"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + set_task_state(tsk, TASK_INTERRUPTIBLE); + + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + ret = 0; + for (;;) { + if (!waiter.task) + break; + if (unlikely(signal_pending(current))) + goto interrupted; + schedule(); + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + out: + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down_interruptible"); + return ret; + + interrupted: + spin_lock_irqsave(&sem->wait_lock, flags); + list_del(&waiter.list); + spin_unlock_irqrestore(&sem->wait_lock, flags); + + ret = 0; + if (!waiter.task) { + put_task_struct(current); + ret = -EINTR; + } + goto out; +} +EXPORT_SYMBOL(__down_interruptible); + +/* + * release a single token back to a semaphore + * - entered with lock held and interrupts disabled + */ +void __up(struct semaphore *sem) +{ + struct task_struct *tsk; + struct sem_waiter *waiter; + + semtrace(sem, "Entering __up"); + + /* grant the token to the process at the front of the queue */ + waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); + + /* We must be careful not to touch 'waiter' after we set ->task = NULL. + * It is an allocated on the waiter's stack and may become invalid at + * any time after that point (due to a wakeup from another source). + */ + list_del_init(&waiter->list); + tsk = waiter->task; + smp_mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + + semtrace(sem, "Leaving __up"); +} +EXPORT_SYMBOL(__up); diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c new file mode 100644 index 00000000000..6b7ce263685 --- /dev/null +++ b/arch/mn10300/kernel/setup.c @@ -0,0 +1,298 @@ +/* MN10300 Arch-specific initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/seq_file.h> +#include <asm/processor.h> +#include <linux/console.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/setup.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/proc/proc.h> +#include <asm/busctl-regs.h> +#include <asm/fpu.h> +#include <asm/sections.h> + +struct mn10300_cpuinfo boot_cpu_data; + +/* For PCI or other memory-mapped resources */ +unsigned long pci_mem_start = 0x18000000; + +char redboot_command_line[COMMAND_LINE_SIZE] = + "console=ttyS0,115200 root=/dev/mtdblock3 rw"; + +char __initdata redboot_platform_name[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { + .start = 0x100000, + .end = 0, + .name = "Kernel code", +}; + +static struct resource data_resource = { + .start = 0, + .end = 0, + .name = "Kernel data", +}; + +static unsigned long __initdata phys_memory_base; +static unsigned long __initdata phys_memory_end; +static unsigned long __initdata memory_end; +unsigned long memory_size; + +struct thread_info *__current_ti = &init_thread_union.thread_info; +struct task_struct *__current = &init_task; + +#define mn10300_known_cpus 3 +static const char *const mn10300_cputypes[] = { + "am33v1", + "am33v2", + "am34v1", + "unknown" +}; + +/* + * + */ +static void __init parse_mem_cmdline(char **cmdline_p) +{ + char *from, *to, c; + + /* save unparsed command line copy for /proc/cmdline */ + strcpy(boot_command_line, redboot_command_line); + + /* see if there's an explicit memory size option */ + from = redboot_command_line; + to = redboot_command_line; + c = ' '; + + for (;;) { + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != redboot_command_line) + to--; + memory_size = memparse(from + 4, &from); + } + + c = *(from++); + if (!c) + break; + + *(to++) = c; + } + + *to = '\0'; + *cmdline_p = redboot_command_line; + + if (memory_size == 0) + panic("Memory size not known\n"); + + memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS + + memory_size; + if (memory_end > phys_memory_end) + memory_end = phys_memory_end; +} + +/* + * architecture specific setup + */ +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long kstart_pfn, start_pfn, free_pfn, end_pfn; + + cpu_init(); + unit_setup(); + parse_mem_cmdline(cmdline_p); + + init_mm.start_code = (unsigned long)&_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + + start_pfn = (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT); + kstart_pfn = PFN_UP(__pa(&_text)); + free_pfn = PFN_UP(__pa(&_end)); + end_pfn = PFN_DOWN(__pa(memory_end)); + + bootmap_size = init_bootmem_node(&contig_page_data, + free_pfn, + start_pfn, + end_pfn); + + if (kstart_pfn > start_pfn) + free_bootmem(PFN_PHYS(start_pfn), + PFN_PHYS(kstart_pfn - start_pfn)); + + free_bootmem(PFN_PHYS(free_pfn), + PFN_PHYS(end_pfn - free_pfn)); + + /* If interrupt vector table is in main ram, then we need to + reserve the page it is occupying. */ + if (CONFIG_INTERRUPT_VECTOR_BASE >= CONFIG_KERNEL_RAM_BASE_ADDRESS && + CONFIG_INTERRUPT_VECTOR_BASE < memory_end) + reserve_bootmem(CONFIG_INTERRUPT_VECTOR_BASE, 1, + BOOTMEM_DEFAULT); + + reserve_bootmem(PAGE_ALIGN(PFN_PHYS(free_pfn)), bootmap_size, + BOOTMEM_DEFAULT); + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + + paging_init(); +} + +/* + * perform CPU initialisation + */ +void __init cpu_init(void) +{ + unsigned long cpurev = CPUREV, type; + unsigned long base, size; + + type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + printk(KERN_INFO "Matsushita %s, rev %ld\n", + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S); + + /* determine the memory size and base from the memory controller regs */ + memory_size = 0; + + base = SDBASE(0); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[0]: %luMb @%08lx\n", size >> 20, base); + memory_size += size; + phys_memory_base = base; + } + + base = SDBASE(1); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[1]: %luMb @%08lx\n", size >> 20, base); + memory_size += size; + if (phys_memory_base == 0) + phys_memory_base = base; + } + + phys_memory_end = phys_memory_base + memory_size; + +#ifdef CONFIG_FPU + fpu_init_state(); +#endif +} + +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned long cpurev = CPUREV, type, icachesz, dcachesz; + + type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + icachesz = + ((cpurev & CPUREV_ICWAY ) >> CPUREV_ICWAY_S) * + ((cpurev & CPUREV_ICSIZE) >> CPUREV_ICSIZE_S) * + 1024; + + dcachesz = + ((cpurev & CPUREV_DCWAY ) >> CPUREV_DCWAY_S) * + ((cpurev & CPUREV_DCSIZE) >> CPUREV_DCSIZE_S) * + 1024; + + seq_printf(m, + "processor : 0\n" + "vendor_id : Matsushita\n" + "cpu core : %s\n" + "cpu rev : %lu\n" + "model name : " PROCESSOR_MODEL_NAME "\n" + "icache size: %lu\n" + "dcache size: %lu\n", + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S, + icachesz, + dcachesz + ); + + seq_printf(m, + "ioclk speed: %lu.%02luMHz\n" + "bogomips : %lu.%02lu\n\n", + MN10300_IOCLK / 1000000, + (MN10300_IOCLK / 10000) % 100, + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100 + ); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; diff --git a/arch/mn10300/kernel/sigframe.h b/arch/mn10300/kernel/sigframe.h new file mode 100644 index 00000000000..0decba28ae8 --- /dev/null +++ b/arch/mn10300/kernel/sigframe.h @@ -0,0 +1,33 @@ +/* MN10300 Signal frame definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct sigframe +{ + void (*pretcode)(void); + int sig; + struct sigcontext *psc; + struct sigcontext sc; + struct fpucontext fpuctx; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + void (*pretcode)(void); + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + struct fpucontext fpuctx; + char retcode[8]; +}; diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c new file mode 100644 index 00000000000..841ca9955a1 --- /dev/null +++ b/arch/mn10300/kernel/signal.c @@ -0,0 +1,564 @@ +/* MN10300 Signal handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/tty.h> +#include <linux/personality.h> +#include <linux/suspend.h> +#include <asm/cacheflush.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include "sigframe.h" + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage long sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} + +/* + * set signal action syscall + */ +asmlinkage long sys_sigaction(int sig, + const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * set alternate signal stack syscall + */ +asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, __frame->sp); +} + +/* + * do a signal return; undo the signal stack. + */ +static int restore_sigcontext(struct pt_regs *regs, + struct sigcontext __user *sc, long *_d0) +{ + unsigned int err = 0; + + if (is_using_fpu(current)) + fpu_kill_state(current); + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(pc); +#undef COPY + + { + unsigned int tmpflags; +#ifndef CONFIG_MN10300_USING_JTAG +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_T | EPSW_nAR) +#else +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_nAR) +#endif + err |= __get_user(tmpflags, &sc->epsw); + regs->epsw = (regs->epsw & ~USER_EPSW) | + (tmpflags & USER_EPSW); + regs->orig_d0 = -1; /* disable syscall checks */ + } + + { + struct fpucontext *buf; + err |= __get_user(buf, &sc->fpucontext); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= fpu_restore_sigcontext(buf); + } + } + + err |= __get_user(*_d0, &sc->d0); + return err; + +badframe: + return 1; +} + +/* + * standard signal return syscall + */ +asmlinkage long sys_sigreturn(void) +{ + struct sigframe __user *frame = (struct sigframe __user *) __frame->sp; + sigset_t set; + long d0; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask)) + goto badframe; + + if (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(__frame, &frame->sc, &d0)) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * realtime signal return syscall + */ +asmlinkage long sys_rt_sigreturn(void) +{ + struct rt_sigframe __user *frame = + (struct rt_sigframe __user *) __frame->sp; + sigset_t set; + unsigned long d0; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(__frame, &frame->uc.uc_mcontext, &d0)) + goto badframe; + + if (do_sigaltstack(&frame->uc.uc_stack, NULL, __frame->sp) == -EFAULT) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * store the userspace context into a signal frame + */ +static int setup_sigcontext(struct sigcontext __user *sc, + struct fpucontext *fpuctx, + struct pt_regs *regs, + unsigned long mask) +{ + int tmp, err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->x) + COPY(d0); COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(epsw); COPY(pc); +#undef COPY + + tmp = fpu_setup_sigcontext(fpuctx); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * determine which stack to use.. + */ +static inline void __user *get_sigframe(struct k_sigaction *ka, + struct pt_regs *regs, + size_t frame_size) +{ + unsigned long sp; + + /* default to using normal stack */ + sp = regs->sp; + + /* this is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (!on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void __user *) ((sp - frame_size) & ~7UL); +} + +/* + * set up a normal signal frame + */ +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) +{ + struct sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) < 0 || + __put_user(&frame->sc, &frame->psc) < 0) + goto give_sigsegv; + + if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0])) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + goto give_sigsegv; + } + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void (*)(void))frame->retcode, + &frame->pretcode)) + goto give_sigsegv; + /* this is mov $,d0; syscall 0 */ + if (__put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + flush_icache_range((unsigned long) frame->retcode, + (unsigned long) frame->retcode + 5); + } + + /* set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (unsigned long) &frame->sc; + + set_fs(USER_DS); + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sig(SIGSEGV, current); + return -EFAULT; +} + +/* + * set up a realtime signal frame + */ +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) || + __put_user(&frame->info, &frame->pinfo) || + __put_user(&frame->uc, &frame->puc) || + copy_siginfo_to_user(&frame->info, info)) + goto give_sigsegv; + + /* create the ucontext. */ + if (__put_user(0, &frame->uc.uc_flags) || + __put_user(0, &frame->uc.uc_link) || + __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) || + __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) || + __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size) || + setup_sigcontext(&frame->uc.uc_mcontext, + &frame->fpuctx, regs, set->sig[0]) || + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) + goto give_sigsegv; + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void(*)(void))frame->retcode, + &frame->pretcode) || + /* This is mov $,d0; syscall 0 */ + __put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_rt_sigreturn, + (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + + flush_icache_range((u_long) frame->retcode, + (u_long) frame->retcode + 5); + } + + /* Set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (long) &frame->info; + + set_fs(USER_DS); + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sig(SIGSEGV, current); + return -EFAULT; +} + +/* + * handle the actual delivery of a signal to userspace + */ +static int handle_signal(int sig, + siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs *regs) +{ + int ret; + + /* Are we from a system call? */ + if (regs->orig_d0 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->d0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + + /* fallthrough */ + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(sig, ka, info, oldset, regs); + else + ret = setup_frame(sig, ka, oldset, regs); + + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; +} + +/* + * handle a potential signal + */ +static void do_signal(struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + sigset_t *oldset; + int signr; + + /* we want the common case to go fast, which is why we may in certain + * cases get here from kernel mode */ + if (!user_mode(regs)) + return; + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; + } + + /* did we come from a system call? */ + if (regs->orig_d0 >= 0) { + /* restart the system call - no handlers present */ + switch (regs->d0) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + break; + + case -ERESTART_RESTARTBLOCK: + regs->d0 = __NR_restart_syscall; + regs->pc -= 2; + break; + } + } + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) +{ + /* Pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) { +#ifndef CONFIG_MN10300_USING_JTAG + regs->epsw |= EPSW_T; + clear_thread_flag(TIF_SINGLESTEP); +#else + BUG(); /* no h/w single-step if using JTAG unit */ +#endif + } + + /* deal with pending signal delivery */ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(regs); +} diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S new file mode 100644 index 00000000000..630aad71b94 --- /dev/null +++ b/arch/mn10300/kernel/switch_to.S @@ -0,0 +1,71 @@ +############################################################################### +# +# MN10300 Context switch operation +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> + + .text + +############################################################################### +# +# struct task_struct *__switch_to(struct thread_struct *prev, +# struct thread_struct *next, +# struct task_struct *prev_task) +# +############################################################################### +ENTRY(__switch_to) + movm [d2,d3,a2,a3,exreg1],(sp) + or EPSW_NMID,epsw + + mov (44,sp),d2 + + mov d0,a0 + mov d1,a1 + + # save prev context + mov (__frame),d0 + mov d0,(THREAD_FRAME,a0) + mov __switch_back,d0 + mov d0,(THREAD_PC,a0) + mov sp,a2 + mov a2,(THREAD_SP,a0) + mov a3,(THREAD_A3,a0) + + mov (THREAD_A3,a1),a3 + mov (THREAD_SP,a1),a2 + + # switch + mov a2,sp + + # load next context + GET_THREAD_INFO a2 + mov a2,(__current_ti) + mov (TI_task,a2),a2 + mov a2,(__current) +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov a2,e2 +#endif + + mov (THREAD_FRAME,a1),a2 + mov a2,(__frame) + mov (THREAD_PC,a1),a2 + mov d2,d0 # for ret_from_fork + mov d0,a0 # for __switch_to + + jmp (a2) + +__switch_back: + and ~EPSW_NMID,epsw + ret [d2,d3,a2,a3,exreg1],32 diff --git a/arch/mn10300/kernel/sys_mn10300.c b/arch/mn10300/kernel/sys_mn10300.c new file mode 100644 index 00000000000..5f17a1ebc82 --- /dev/null +++ b/arch/mn10300/kernel/sys_mn10300.c @@ -0,0 +1,193 @@ +/* MN10300 Weird system calls + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> +#include <linux/syscalls.h> +#include <linux/tty.h> + +#include <asm/uaccess.h> + +#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */ + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage long sys_pipe(unsigned long __user *fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2 * sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* + * memory mapping syscall + */ +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + struct file *file = NULL; + long error = -EINVAL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + if (flags & MAP_FIXED && addr < MIN_MAP_ADDR) + goto out; + + error = -EBADF; + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset) +{ + if (offset & ~PAGE_MASK) + return -EINVAL; + return sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp; + fd_set *outp; + fd_set *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct __user *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage long sys_ipc(uint call, int first, int second, + int third, void __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, + (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget(first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user * __user *) ptr)) + return -EFAULT; + return sys_semctl(first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd(first, (struct msgbuf __user *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof(tmp))) + return -EFAULT; + return sys_msgrcv(first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv(first, + (struct msgbuf __user *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget((key_t) first, second); + case MSGCTL: + return sys_msgctl(first, second, + (struct msqid_ds __user *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = do_shmat(first, (char __user *) ptr, second, + &raddr); + if (ret) + return ret; + return put_user(raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return do_shmat(first, (char __user *) ptr, second, + (ulong *) third); + } + case SHMDT: + return sys_shmdt((char __user *)ptr); + case SHMGET: + return sys_shmget(first, second, third); + case SHMCTL: + return sys_shmctl(first, second, + (struct shmid_ds __user *) ptr); + default: + return -EINVAL; + } +} diff --git a/arch/mn10300/kernel/time.c b/arch/mn10300/kernel/time.c new file mode 100644 index 00000000000..ff492e3b345 --- /dev/null +++ b/arch/mn10300/kernel/time.c @@ -0,0 +1,129 @@ +/* MN10300 Low level time management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/time.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/profile.h> +#include <asm/irq.h> +#include <asm/div64.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc.h> + +#ifdef CONFIG_MN10300_RTC +unsigned long mn10300_ioclk; /* system I/O clock frequency */ +unsigned long mn10300_iobclk; /* system I/O clock frequency */ +unsigned long mn10300_tsc_per_HZ; /* number of ioclks per jiffy */ +#endif /* CONFIG_MN10300_RTC */ + +static unsigned long mn10300_last_tsc; /* time-stamp counter at last time + * interrupt occurred */ + +static irqreturn_t timer_interrupt(int irq, void *dev_id); + +static struct irqaction timer_irq = { + .handler = timer_interrupt, + .flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER, + .mask = CPU_MASK_NONE, + .name = "timer", +}; + +/* + * scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + union { + unsigned long long l; + u32 w[2]; + } quot; + + quot.w[0] = mn10300_last_tsc - get_cycles(); + quot.w[1] = 1000000000; + + asm("mulu %2,%3,%0,%1" + : "=r"(quot.w[1]), "=r"(quot.w[0]) + : "0"(quot.w[1]), "1"(quot.w[0]) + : "cc"); + + do_div(quot.l, MN10300_TSCCLK); + + return quot.l; +} + +/* + * advance the kernel's time keeping clocks (xtime and jiffies) + * - we use Timer 0 & 1 cascaded as a clock to nudge us the next time + * there's a need to update + */ +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + unsigned tsc, elapse; + + write_seqlock(&xtime_lock); + + while (tsc = get_cycles(), + elapse = mn10300_last_tsc - tsc, /* time elapsed since last + * tick */ + elapse > MN10300_TSC_PER_HZ + ) { + mn10300_last_tsc -= MN10300_TSC_PER_HZ; + + /* advance the kernel's time tracking system */ + profile_tick(CPU_PROFILING); + do_timer(1); + update_process_times(user_mode(get_irq_regs())); + check_rtc_time(); + } + + write_sequnlock(&xtime_lock); + return IRQ_HANDLED; +} + +/* + * initialise the various timers used by the main part of the kernel + */ +void __init time_init(void) +{ + /* we need the prescalar running to be able to use IOCLK/8 + * - IOCLK runs at 1/4 (ST5 open) or 1/8 (ST5 closed) internal CPU clock + * - IOCLK runs at Fosc rate (crystal speed) + */ + TMPSCNT |= TMPSCNT_ENABLE; + + startup_timestamp_counter(); + + printk(KERN_INFO + "timestamp counter I/O clock running at %lu.%02lu" + " (calibrated against RTC)\n", + MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100); + + xtime.tv_sec = get_initial_rtc_time(); + xtime.tv_nsec = 0; + + mn10300_last_tsc = TMTSCBC; + + /* use timer 0 & 1 cascaded to tick at as close to HZ as possible */ + setup_irq(TMJCIRQ, &timer_irq); + + set_intr_level(TMJCIRQ, TMJCICR_LEVEL); + + startup_jiffies_counter(); + +#ifdef CONFIG_MN10300_WD_TIMER + /* start the watchdog timer */ + watchdog_go(); +#endif +} diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c new file mode 100644 index 00000000000..8b9dc6d9dcc --- /dev/null +++ b/arch/mn10300/kernel/traps.c @@ -0,0 +1,619 @@ +/* MN10300 Exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/kallsyms.h> +#include <linux/pci.h> +#include <linux/kdebug.h> +#include <linux/bug.h> +#include <linux/irq.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cacheflush.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <asm/unit/leds.h> +#include <asm/fpu.h> +#include <asm/gdb-stub.h> +#include <asm/sections.h> + +#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff) +#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!" +#endif + +struct pt_regs *__frame; /* current frame pointer */ +EXPORT_SYMBOL(__frame); + +int kstack_depth_to_print = 24; + +spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock); + +ATOMIC_NOTIFIER_HEAD(mn10300_die_chain); + +/* + * These constants are for searching for possible module text + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. + */ +#define MODULE_RANGE (8 * 1024 * 1024) + +#define DO_ERROR(signr, prologue, str, name) \ +asmlinkage void name(struct pt_regs *regs, u32 intcode) \ +{ \ + prologue; \ + if (die_if_no_fixup(str, regs, intcode)) \ + return; \ + force_sig(signr, current); \ +} + +#define DO_EINFO(signr, prologue, str, name, sicode) \ +asmlinkage void name(struct pt_regs *regs, u32 intcode) \ +{ \ + siginfo_t info; \ + prologue; \ + if (die_if_no_fixup(str, regs, intcode)) \ + return; \ + info.si_signo = signr; \ + if (signr == SIGILL && sicode == ILL_ILLOPC) { \ + uint8_t opcode; \ + if (get_user(opcode, (uint8_t __user *)regs->pc) == 0) \ + if (opcode == 0xff) \ + info.si_signo = SIGTRAP; \ + } \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *) regs->pc; \ + force_sig_info(info.si_signo, &info, current); \ +} + +DO_ERROR(SIGTRAP, {}, "trap", trap); +DO_ERROR(SIGSEGV, {}, "ibreak", ibreak); +DO_ERROR(SIGSEGV, {}, "obreak", obreak); +DO_EINFO(SIGSEGV, {}, "access error", access_error, SEGV_ACCERR); +DO_EINFO(SIGSEGV, {}, "insn access error", insn_acc_error, SEGV_ACCERR); +DO_EINFO(SIGSEGV, {}, "data access error", data_acc_error, SEGV_ACCERR); +DO_EINFO(SIGILL, {}, "privileged opcode", priv_op, ILL_PRVOPC); +DO_EINFO(SIGILL, {}, "invalid opcode", invalid_op, ILL_ILLOPC); +DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC); +DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR); +DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR); +DO_EINFO(SIGILL, {}, "FPU invalid opcode", fpu_invalid_op, ILL_COPROC); + +DO_ERROR(SIGTRAP, +#ifndef CONFIG_MN10300_USING_JTAG + DCR &= ~0x0001, +#else + {}, +#endif + "single step", istep); + +/* + * handle NMI + */ +asmlinkage void nmi(struct pt_regs *regs, enum exception_code code) +{ + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return; +#endif + + printk(KERN_WARNING "--- Register Dump ---\n"); + show_registers(regs); + printk(KERN_WARNING "---------------------\n"); +} + +/* + * show a stack trace from the specified stack pointer + */ +void show_trace(unsigned long *sp) +{ + unsigned long *stack, addr, module_start, module_end; + int i; + + printk(KERN_EMERG "\n" + KERN_EMERG "Call Trace:"); + + stack = sp; + i = 0; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + + while (((long) stack & (THREAD_SIZE - 1)) != 0) { + addr = *stack++; + if (__kernel_text_address(addr)) { +#if 1 + printk(" [<%08lx>]", addr); + print_symbol(" %s", addr); + printk("\n"); +#else + if ((i % 6) == 0) + printk("\n" KERN_EMERG " "); + printk("[<%08lx>] ", addr); + i++; +#endif + } + } + + printk("\n"); +} + +/* + * show the raw stack from the specified stack pointer + */ +void show_stack(struct task_struct *task, unsigned long *sp) +{ + unsigned long *stack; + int i; + + if (!sp) + sp = (unsigned long *) &sp; + + stack = sp; + printk(KERN_EMERG "Stack:"); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE - 1)) == 0) + break; + if ((i % 8) == 0) + printk("\n" KERN_EMERG " "); + printk("%08lx ", *stack++); + } + + show_trace(sp); +} + +/* + * the architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} +EXPORT_SYMBOL(dump_stack); + +/* + * dump the register file in the specified exception frame + */ +void show_registers_only(struct pt_regs *regs) +{ + unsigned long ssp; + + ssp = (unsigned long) regs + sizeof(*regs); + + printk(KERN_EMERG "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, ssp, user_mode(regs) ? "User" : "Super"); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + printk(KERN_EMERG "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + printk(KERN_EMERG "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + printk(KERN_EMERG "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + printk(KERN_EMERG "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + printk(KERN_EMERG "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + printk(KERN_EMERG "threadinfo=%p task=%p)\n", + current_thread_info(), current); + + if ((unsigned long) current >= 0x90000000UL && + (unsigned long) current < 0x94000000UL) + printk(KERN_EMERG "Process %s (pid: %d)\n", + current->comm, current->pid); + + printk(KERN_EMERG "CPUP: %04hx\n", CPUP); + printk(KERN_EMERG "TBR: %08x\n", TBR); + printk(KERN_EMERG "DEAR: %08x\n", DEAR); + printk(KERN_EMERG "sISR: %08x\n", sISR); + printk(KERN_EMERG "NMICR: %04hx\n", NMICR); + printk(KERN_EMERG "BCBERR: %08x\n", BCBERR); + printk(KERN_EMERG "BCBEAR: %08x\n", BCBEAR); + printk(KERN_EMERG "MMUFCR: %08x\n", MMUFCR); + printk(KERN_EMERG "IPTEU : %08x IPTEL2: %08x\n", IPTEU, IPTEL2); + printk(KERN_EMERG "DPTEU: %08x DPTEL2: %08x\n", DPTEU, DPTEL2); +} + +/* + * dump the registers and the stack + */ +void show_registers(struct pt_regs *regs) +{ + unsigned long sp; + int i; + + show_registers_only(regs); + + if (!user_mode(regs)) + sp = (unsigned long) regs + sizeof(*regs); + else + sp = regs->sp; + + /* when in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + printk(KERN_EMERG "\n"); + show_stack(current, (unsigned long *) sp); + +#if 0 + printk(KERN_EMERG "\n" + KERN_EMERG "Code: "); + if (regs->pc < PAGE_OFFSET) + goto bad; + + for (i = 0; i < 20; i++) { + unsigned char c; + if (__get_user(c, &((unsigned char *) regs->pc)[i])) + goto bad; + printk("%02x ", c); + } +#else + i = 0; +#endif + } + + printk("\n"); + return; + +#if 0 +bad: + printk(KERN_EMERG " Bad PC value."); + break; +#endif +} + +/* + * + */ +void show_trace_task(struct task_struct *tsk) +{ + unsigned long sp = tsk->thread.sp; + + /* User space on another CPU? */ + if ((sp ^ (unsigned long) tsk) & (PAGE_MASK << 1)) + return; + + show_trace((unsigned long *) sp); +} + +/* + * note the untimely death of part of the kernel + */ +void die(const char *str, struct pt_regs *regs, enum exception_code code) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk(KERN_EMERG "\n" + KERN_EMERG "%s: %04x\n", + str, code & 0xffff); + show_registers(regs); + + if (regs->pc >= 0x02000000 && regs->pc < 0x04000000 && + (regs->epsw & (EPSW_IM | EPSW_IE)) != (EPSW_IM | EPSW_IE)) { + printk(KERN_EMERG "Exception in usermode interrupt handler\n"); + printk(KERN_EMERG "\n" + KERN_EMERG " Please connect to kernel debugger !!\n"); + asm volatile ("0: bra 0b"); + } + + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +/* + * see if there's a fixup handler we can force a jump to when an exception + * happens due to something kernel code did + */ +int die_if_no_fixup(const char *str, struct pt_regs *regs, + enum exception_code code) +{ + if (user_mode(regs)) + return 0; + + peripheral_leds_display_exception(code); + + switch (code) { + /* see if we can fixup the kernel accessing memory */ + case EXCEP_ITLBMISS: + case EXCEP_DTLBMISS: + case EXCEP_IAERROR: + case EXCEP_DAERROR: + case EXCEP_MEMERR: + case EXCEP_MISALIGN: + case EXCEP_BUSERROR: + case EXCEP_ILLDATACC: + case EXCEP_IOINSACC: + case EXCEP_PRIVINSACC: + case EXCEP_PRIVDATACC: + case EXCEP_DATINSACC: + if (fixup_exception(regs)) + return 1; + case EXCEP_UNIMPINS: + if (regs->pc && *(uint8_t *)regs->pc == 0xff) + if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0)) + return 1; + break; + default: + break; + } + + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return 1; +#endif + + if (notify_die(DIE_GPF, str, regs, code, 0, 0)) + return 1; + + /* make the process die as the last resort */ + die(str, regs, code); +} + +/* + * handle unsupported syscall instructions (syscall 1-15) + */ +static asmlinkage void unsupported_syscall(struct pt_regs *regs, + enum exception_code code) +{ + struct task_struct *tsk = current; + siginfo_t info; + + /* catch a kernel BUG() */ + if (code == EXCEP_SYSCALL15 && !user_mode(regs)) { + if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) { +#ifdef CONFIG_GDBSTUB + __gdbstub_bug_trap(); +#endif + } + } + + regs->pc -= 2; /* syscall return addr is _after_ the instruction */ + + die_if_no_fixup("An unsupported syscall insn was used by the kernel\n", + regs, code); + + info.si_signo = SIGILL; + info.si_errno = ENOSYS; + info.si_code = ILL_ILLTRP; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGILL, &info, tsk); +} + +/* + * display the register file when the stack pointer gets clobbered + */ +asmlinkage void do_double_fault(struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + strcpy(tsk->comm, "emergency tsk"); + tsk->pid = 0; + console_verbose(); + printk(KERN_EMERG "--- double fault ---\n"); + show_registers(regs); +} + +/* + * asynchronous bus error (external, usually I/O DMA) + */ +asmlinkage void io_bus_error(u32 bcberr, u32 bcbear, struct pt_regs *regs) +{ + console_verbose(); + + printk(KERN_EMERG "\n" + KERN_EMERG "Asynchronous I/O Bus Error\n" + KERN_EMERG "==========================\n"); + + if (bcberr & BCBERR_BEME) + printk(KERN_EMERG "- Multiple recorded errors\n"); + + printk(KERN_EMERG "- Faulting Buses:%s%s%s\n", + bcberr & BCBERR_BEMR_CI ? " CPU-Ins-Fetch" : "", + bcberr & BCBERR_BEMR_CD ? " CPU-Data" : "", + bcberr & BCBERR_BEMR_DMA ? " DMA" : ""); + + printk(KERN_EMERG "- %s %s access made to %s at address %08x\n", + bcberr & BCBERR_BEBST ? "Burst" : "Single", + bcberr & BCBERR_BERW ? "Read" : "Write", + bcberr & BCBERR_BESB_MON ? "Monitor Space" : + bcberr & BCBERR_BESB_IO ? "Internal CPU I/O Space" : + bcberr & BCBERR_BESB_EX ? "External I/O Bus" : + bcberr & BCBERR_BESB_OPEX ? "External Memory Bus" : + "On Chip Memory", + bcbear + ); + + printk(KERN_EMERG "- Detected by the %s\n", + bcberr&BCBERR_BESD ? "Bus Control Unit" : "Slave Bus"); + +#ifdef CONFIG_PCI +#define BRIDGEREGB(X) (*(volatile __u8 *)(0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *)(0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *)(0xBE040000 + (X))) + + printk(KERN_EMERG "- PCI Memory Paging Reg: %08x\n", + *(volatile __u32 *) (0xBFFFFFF4)); + printk(KERN_EMERG "- PCI Bridge Base Address 0: %08x\n", + BRIDGEREGL(PCI_BASE_ADDRESS_0)); + printk(KERN_EMERG "- PCI Bridge AMPCI Base Address: %08x\n", + BRIDGEREGL(0x48)); + printk(KERN_EMERG "- PCI Bridge Command: %04hx\n", + BRIDGEREGW(PCI_COMMAND)); + printk(KERN_EMERG "- PCI Bridge Status: %04hx\n", + BRIDGEREGW(PCI_STATUS)); + printk(KERN_EMERG "- PCI Bridge Int Status: %08hx\n", + BRIDGEREGL(0x4c)); +#endif + + printk(KERN_EMERG "\n"); + show_registers(regs); + + panic("Halted due to asynchronous I/O Bus Error\n"); +} + +/* + * handle an exception for which a handler has not yet been installed + */ +asmlinkage void uninitialised_exception(struct pt_regs *regs, + enum exception_code code) +{ + + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return; +#endif + + peripheral_leds_display_exception(code); + printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF); + show_registers(regs); + + for (;;) + continue; +} + +/* + * set an interrupt stub to jump to a handler + * ! NOTE: this does *not* flush the caches + */ +void __init __set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - (unsigned long) vector; + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; +} + +/* + * set an interrupt stub to jump to a handler + */ +void __init set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - (unsigned long) vector; + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; + + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); +} + +/* + * set an interrupt stub to invoke the JTAG unit and then jump to a handler + */ +void __init set_jtag_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - ((unsigned long) vector + 1); + vector[0] = 0xff; /* PI to jump into JTAG debugger */ + vector[1] = 0xdc; /* jmp handler */ + vector[2] = addr; + vector[3] = addr >> 8; + vector[4] = addr >> 16; + vector[5] = addr >> 24; + vector[6] = 0xcb; + vector[7] = 0xcb; + + mn10300_dcache_flush_inv(); + flush_icache_range((unsigned long) vector, (unsigned long) vector + 8); +} + +/* + * initialise the exception table + */ +void __init trap_init(void) +{ + set_excp_vector(EXCEP_TRAP, trap); + set_excp_vector(EXCEP_ISTEP, istep); + set_excp_vector(EXCEP_IBREAK, ibreak); + set_excp_vector(EXCEP_OBREAK, obreak); + + set_excp_vector(EXCEP_PRIVINS, priv_op); + set_excp_vector(EXCEP_UNIMPINS, invalid_op); + set_excp_vector(EXCEP_UNIMPEXINS, invalid_exop); + set_excp_vector(EXCEP_MEMERR, mem_error); + set_excp_vector(EXCEP_MISALIGN, misalignment); + set_excp_vector(EXCEP_BUSERROR, bus_error); + set_excp_vector(EXCEP_ILLINSACC, insn_acc_error); + set_excp_vector(EXCEP_ILLDATACC, data_acc_error); + set_excp_vector(EXCEP_IOINSACC, insn_acc_error); + set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error); + set_excp_vector(EXCEP_PRIVDATACC, data_acc_error); + set_excp_vector(EXCEP_DATINSACC, insn_acc_error); + set_excp_vector(EXCEP_FPU_DISABLED, fpu_disabled); + set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op); + set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception); + + set_excp_vector(EXCEP_NMI, nmi); + + set_excp_vector(EXCEP_SYSCALL1, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL2, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL3, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL4, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL5, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL6, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL7, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL8, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL9, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL10, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL11, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL12, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL13, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL14, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL15, unsupported_syscall); +} + +/* + * determine if a program counter value is a valid bug address + */ +int is_valid_bugaddr(unsigned long pc) +{ + return pc >= PAGE_OFFSET; +} diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..a3e80f444f5 --- /dev/null +++ b/arch/mn10300/kernel/vmlinux.lds.S @@ -0,0 +1,159 @@ +/* MN10300 Main kernel linker script + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define __VMLINUX_LDS__ +#include <asm-generic/vmlinux.lds.h> +#include <asm/thread_info.h> + +OUTPUT_FORMAT("elf32-am33lin", "elf32-am33lin", "elf32-am33lin") +OUTPUT_ARCH(mn10300) +ENTRY(_start) +jiffies = jiffies_64; +#ifndef CONFIG_MN10300_CURRENT_IN_E2 +current = __current; +#endif +SECTIONS +{ + . = CONFIG_KERNEL_TEXT_ADDRESS; + /* read-only */ + _stext = .; + _text = .; /* Text and read-only data */ + .text : { + *( + .text.head + .text + ) + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) + } = 0xcb + + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + BUG_TABLE + + RODATA + + /* writeable */ + .data : { /* Data */ + DATA_DATA + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + /* rarely changed data like cpu maps */ + . = ALIGN(32); + .data.read_mostly : AT(ADDR(.data.read_mostly)) { + *(.data.read_mostly) + _edata = .; /* End of data section */ + } + + . = ALIGN(THREAD_SIZE); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* might get freed after init */ + . = ALIGN(4096); + .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) { + __smp_locks = .; + *(.smp_locks) + __smp_locks_end = .; + } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { KEEP(*(.init.setup)) } + __setup_end = .; + + __initcall_start = .; + .initcall.init : { + INITCALLS + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + + SECURITY_INIT + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { *(.exit.text) } + .exit.data : { *(.exit.data) } + +#ifdef CONFIG_BLK_DEV_INITRD + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; +#endif + + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss.page_aligned) + *(.bss) + } + . = ALIGN(4); + __bss_stop = .; + + _end = . ; + + /* This is where the kernel creates the early boot page tables */ + . = ALIGN(4096); + pg0 = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exitcall.exit) + } + + STABS_DEBUG + + DWARF_DEBUG +} diff --git a/arch/mn10300/lib/Makefile b/arch/mn10300/lib/Makefile new file mode 100644 index 00000000000..fdfa9ec5b5b --- /dev/null +++ b/arch/mn10300/lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the MN10300-specific library files.. +# + +lib-y = delay.o usercopy.o checksum.o bitops.o memcpy.o memmove.o memset.o +lib-y += do_csum.o +lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o diff --git a/arch/mn10300/lib/__ashldi3.S b/arch/mn10300/lib/__ashldi3.S new file mode 100644 index 00000000000..a51a9506f00 --- /dev/null +++ b/arch/mn10300/lib/__ashldi3.S @@ -0,0 +1,51 @@ +/* MN10300 64-bit arithmetic left shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashldi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashldi3 + .type __ashldi3,@function +__ashldi3: + mov (12,sp),a0 + and +63,a0 + beq __ashldi3_zero + + cmp +31,a0 + bhi __ashldi3_32plus + + # the count is in the range 1-31 + asl a0,d1 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + lsr a1,d0,a1 # get overflow from LSW -> MSW + + or_asl a1,d1,a0,d0 # insert overflow into MSW and + # shift the LSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashldi3_32plus: + asl a0,d0,d1 + clr d0 +__ashldi3_zero: + rets + + .size __ashldi3, .-__ashldi3 diff --git a/arch/mn10300/lib/__ashrdi3.S b/arch/mn10300/lib/__ashrdi3.S new file mode 100644 index 00000000000..6f42382728c --- /dev/null +++ b/arch/mn10300/lib/__ashrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit arithmetic right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashrdi3 + .type __ashrdi3,@function +__ashrdi3: + mov (12,sp),a0 + and +63,a0 + beq __ashrdi3_zero + + cmp +31,a0 + bhi __ashrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_asr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashrdi3_32plus: + asr a0,d1,d0 + ext d0 # sign-extend result through MDR + mov mdr,d1 +__ashrdi3_zero: + rets + + .size __ashrdi3, .-__ashrdi3 diff --git a/arch/mn10300/lib/__lshrdi3.S b/arch/mn10300/lib/__lshrdi3.S new file mode 100644 index 00000000000..a686aef31e9 --- /dev/null +++ b/arch/mn10300/lib/__lshrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit logical right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __lshrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __lshrdi3 + .type __lshrdi3,@function +__lshrdi3: + mov (12,sp),a0 + and +63,a0 + beq __lshrdi3_zero + + cmp +31,a0 + bhi __lshrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_lsr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__lshrdi3_32plus: + lsr a0,d1,d0 + clr d1 +__lshrdi3_zero: + rets + + .size __lshrdi3, .-__lshrdi3 diff --git a/arch/mn10300/lib/ashrdi3.c b/arch/mn10300/lib/ashrdi3.c new file mode 100644 index 00000000000..c54f61ddf0b --- /dev/null +++ b/arch/mn10300/lib/ashrdi3.c @@ -0,0 +1,61 @@ +/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __ashrdi3(DItype u, word_type b) +{ + union DIunion w; + union DIunion uu; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof(SItype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } else { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/bitops.c b/arch/mn10300/lib/bitops.c new file mode 100644 index 00000000000..440a7dcbf87 --- /dev/null +++ b/arch/mn10300/lib/bitops.c @@ -0,0 +1,51 @@ +/* MN10300 Non-trivial bit operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/bitops.h> +#include <asm/system.h> + +/* + * try flipping a bit using BSET and BCLR + */ +void change_bit(int nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return; + + goto try_set_bit; +} + +/* + * try flipping a bit using BSET and BCLR and returning the old value + */ +int test_and_change_bit(int nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return 0; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return 1; + + goto try_set_bit; +} diff --git a/arch/mn10300/lib/checksum.c b/arch/mn10300/lib/checksum.c new file mode 100644 index 00000000000..274f29ec33c --- /dev/null +++ b/arch/mn10300/lib/checksum.c @@ -0,0 +1,99 @@ +/* MN10300 Optimised checksumming wrappers + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> +#include <asm/checksum.h> +#include "internal.h" + +static inline unsigned short from32to16(__wsum sum) +{ + asm(" add %1,%0 \n" + " addc 0xffff,%0 \n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + ); + return sum >> 16; +} + +__sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + return ~do_csum(iph, ihl * 4); +} +EXPORT_SYMBOL(ip_fast_csum); + +__wsum csum_partial(const void *buff, int len, __wsum sum) +{ + __wsum result; + + result = do_csum(buff, len); + result += sum; + if (sum > result) + result++; + return result; +} +EXPORT_SYMBOL(csum_partial); + +__sum16 ip_compute_csum(const void *buff, int len) +{ + return ~from32to16(do_csum(buff, len)); +} +EXPORT_SYMBOL(ip_compute_csum); + +__wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum) +{ + copy_from_user(dst, src, len); + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy); + +__wsum csum_partial_copy_nocheck(const void *src, void *dst, + int len, __wsum sum) +{ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + return sum; +} +EXPORT_SYMBOL(csum_partial_copy_nocheck); + +__wsum csum_partial_copy_from_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy_from_user); + +__wsum csum_and_copy_to_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_to_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(src, len, sum); +} +EXPORT_SYMBOL(csum_and_copy_to_user); diff --git a/arch/mn10300/lib/delay.c b/arch/mn10300/lib/delay.c new file mode 100644 index 00000000000..cce66bc0822 --- /dev/null +++ b/arch/mn10300/lib/delay.c @@ -0,0 +1,50 @@ +/* MN10300 Short delay interpolation routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <asm/div64.h> + +/* + * basic delay loop + */ +void __delay(unsigned long loops) +{ + int d0; + + asm volatile( + " bra 1f \n" + " .align 4 \n" + "1: bra 2f \n" + " .align 4 \n" + "2: add -1,%0 \n" + " bne 2b \n" + : "=&d" (d0) + : "0" (loops)); +} +EXPORT_SYMBOL(__delay); + +/* + * handle a delay specified in terms of microseconds + */ +void __udelay(unsigned long usecs) +{ + signed long ioclk, stop; + + /* usecs * CLK / 1E6 */ + stop = __muldiv64u(usecs, MN10300_TSCCLK, 1000000); + stop = TMTSCBC - stop; + + do { + ioclk = TMTSCBC; + } while (stop < ioclk); +} +EXPORT_SYMBOL(__udelay); diff --git a/arch/mn10300/lib/do_csum.S b/arch/mn10300/lib/do_csum.S new file mode 100644 index 00000000000..e138994e166 --- /dev/null +++ b/arch/mn10300/lib/do_csum.S @@ -0,0 +1,162 @@ +/* Optimised simple memory checksum + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned int do_csum(const unsigned char *buff, size_t len) +# +############################################################################### + .globl do_csum + .type do_csum,@function +do_csum: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov d1,d2 # count + mov d0,a0 # buff + clr d1 # accumulator + + cmp +0,d2 + beq do_csum_done # return if zero-length buffer + + # 4-byte align the buffer pointer + btst +3,a0 + beq do_csum_now_4b_aligned + + btst +1,a0 + beq do_csum_addr_not_odd + movbu (a0),d0 + inc a0 + asl +8,d0 + add d0,d1 + addc +0,d1 + add -1,d2 +do_csum_addr_not_odd: + + cmp +2,d2 + bcs do_csum_fewer_than_4 + btst +2,a0 + beq do_csum_now_4b_aligned + movhu (a0+),d0 + add d0,d1 + addc +0,d1 + add -2,d2 + cmp +4,d2 + bcs do_csum_fewer_than_4 + +do_csum_now_4b_aligned: + # we want to checksum as much as we can in chunks of 32 bytes + cmp +31,d2 + bls do_csum_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +do_csum_loop: + mov (a0+),d0 + add d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + mov (a0+),d0 + addc d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + addc +0,d1 + + sub d3,d2 + bcc do_csum_loop + + add d3,d2 + beq do_csum_done + +do_csum_remainder: + # cut 16-31 bytes down to 0-15 + cmp +16,d2 + bcs do_csum_fewer_than_16 + mov (a0+),d0 + add d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + addc +0,d1 + add -16,d2 + beq do_csum_done + +do_csum_fewer_than_16: + # copy the remaining whole words + cmp +4,d2 + bcs do_csum_fewer_than_4 + cmp +8,d2 + bcs do_csum_one_word + cmp +12,d2 + bcs do_csum_two_words + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_two_words: + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_one_word: + mov (a0+),d0 + add d0,d1 + addc +0,d1 + +do_csum_fewer_than_4: + and +3,d2 + beq do_csum_done + xor_cmp d0,d0,+2,d2 + bcs do_csum_fewer_than_2 + movhu (a0+),d0 +do_csum_fewer_than_2: + and +1,d2 + beq do_csum_add_last_bit + movbu (a0),d3 + add d3,d0 +do_csum_add_last_bit: + add d0,d1 + addc +0,d1 + +do_csum_done: + # compress the checksum down to 16 bits + mov +0xffff0000,d2 + and d1,d2 + asl +16,d1 + add d2,d1,d0 + addc +0xffff,d0 + lsr +16,d0 + + # flip the halves of the word result if the buffer was oddly aligned + mov (12,sp),d1 + and +1,d1 + beq do_csum_not_oddly_aligned + swaph d0,d0 # exchange bits 15:8 with 7:0 + +do_csum_not_oddly_aligned: + ret [d2,d3],8 + +do_csum_end: + .size do_csum, do_csum_end-do_csum diff --git a/arch/mn10300/lib/internal.h b/arch/mn10300/lib/internal.h new file mode 100644 index 00000000000..0014eee5f04 --- /dev/null +++ b/arch/mn10300/lib/internal.h @@ -0,0 +1,15 @@ +/* Internal definitions for the arch part of the kernel library + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * do_csum.S + */ +extern unsigned int do_csum(const unsigned char *, size_t); diff --git a/arch/mn10300/lib/lshrdi3.c b/arch/mn10300/lib/lshrdi3.c new file mode 100644 index 00000000000..e05e64e9ce9 --- /dev/null +++ b/arch/mn10300/lib/lshrdi3.c @@ -0,0 +1,60 @@ +/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC 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 Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __lshrdi3(DItype u, word_type b) +{ + union DIunion w; + word_type bm; + union DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + w.s.high = 0; + w.s.low = (USItype) uu.s.high >> -bm; + } else { + USItype carries = (USItype) uu.s.high << bm; + w.s.high = (USItype) uu.s.high >> b; + w.s.low = ((USItype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/memcpy.S b/arch/mn10300/lib/memcpy.S new file mode 100644 index 00000000000..25fb9bb2604 --- /dev/null +++ b/arch/mn10300/lib/memcpy.S @@ -0,0 +1,135 @@ +/* MN10300 Optimised simple memory to memory copy + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memcpy(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memcpy + .type memcpy,@function +memcpy: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d1,a1 # src + mov d0,e3 # the return value + + cmp +0,d2 + beq memcpy_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memcpy_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memcpy_4_remainder # 4-byte aligned remainder + + movm [exreg1],(sp) + add -32,d2 + mov +32,d3 + +memcpy_4_loop: + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov (a1+),e4 + mov (a1+),e5 + mov (a1+),e6 + mov (a1+),e7 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + mov e4,(a0+) + mov e5,(a0+) + mov e6,(a0+) + mov e7,(a0+) + + sub d3,d2 + bcc memcpy_4_loop + + movm (sp),[exreg1] + add d3,d2 + beq memcpy_4_no_remainder + +memcpy_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memcpy_4_three_or_fewer_words + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + add -16,d2 + beq memcpy_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memcpy_4_three_or_fewer_words: + cmp +8,d2 + bcs memcpy_4_one_word + beq memcpy_4_two_words + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_two_words: + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_one_word: + mov (a1+),d0 + mov d0,(a0+) + +memcpy_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memcpy_done + break + break + break + +memcpy_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memcpy_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + movbu d0,(a0) + add_add d3,a1,d3,a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memcpy_end: + .size memcpy, memcpy_end-memcpy diff --git a/arch/mn10300/lib/memmove.S b/arch/mn10300/lib/memmove.S new file mode 100644 index 00000000000..20b07b62b77 --- /dev/null +++ b/arch/mn10300/lib/memmove.S @@ -0,0 +1,160 @@ +/* MN10300 Optimised simple memory to memory copy, with support for overlapping + * regions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memmove(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memmove + .type memmove,@function +memmove: + # fall back to memcpy if dst < src to work bottom up + cmp d1,d0 + bcs memmove_memcpy + + # work top down + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + add d0,d2,a0 # dst end + add d1,d2,a1 # src end + mov d0,e3 # the return value + + cmp +0,d2 + beq memmove_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memmove_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + add -4,a1 + cmp +31,d2 + bls memmove_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memmove_4_loop: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + sub d3,d2 + bcc memmove_4_loop + + add d3,d2 + beq memmove_4_no_remainder + +memmove_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memmove_4_three_or_fewer_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + mov (a1),e0 + sub_sub +4,a1,+4,a0 + mov e0,(a0) + mov (a1),e1 + sub_sub +4,a1,+4,a0 + mov e1,(a0) + add -16,d2 + beq memmove_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memmove_4_three_or_fewer_words: + cmp +8,d2 + bcs memmove_4_one_word + beq memmove_4_two_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_two_words: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_one_word: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + +memmove_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + beq memmove_done + break + break + break + +memmove_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memmove_1: + add -1,a1 + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + sub_sub d3,a1,d3,a0 + movbu d0,(a0) + lcc + + mov e3,a0 + ret [d2,d3],8 + +memmove_memcpy: + jmp memcpy + +memmove_end: + .size memmove, memmove_end-memmove diff --git a/arch/mn10300/lib/memset.S b/arch/mn10300/lib/memset.S new file mode 100644 index 00000000000..bc02e39629b --- /dev/null +++ b/arch/mn10300/lib/memset.S @@ -0,0 +1,121 @@ +/* Optimised simple memory fill + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memset(void *dst, int c, size_t n) +# +############################################################################### + .globl memset + .type memset,@function +memset: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d0,e3 # the return value + + cmp +0,d2 + beq memset_done # return if zero-length fill + + # see if the region parameters are four-byte aligned + or d0,d2,d3 + and +3,d3 + bne memset_1 # jump if not + + extbu d1 + mov_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or d3,d1 + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memset_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memset_4_loop: + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + + sub d3,d2 + bcc memset_4_loop + + add d3,d2 + beq memset_4_no_remainder + +memset_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memset_4_three_or_fewer_words + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + add -16,d2 + beq memset_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memset_4_three_or_fewer_words: + cmp +8,d2 + bcs memset_4_one_word + beq memset_4_two_words + mov d1,(a0+) +memset_4_two_words: + mov d1,(a0+) +memset_4_one_word: + mov d1,(a0+) + +memset_4_no_remainder: + # check we set the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memset_done + break + break + break + +memset_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memset_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu d1,(a0) + inc a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memset_end: + .size memset, memset_end-memset diff --git a/arch/mn10300/lib/negdi2.c b/arch/mn10300/lib/negdi2.c new file mode 100644 index 00000000000..eae4ecdd5f6 --- /dev/null +++ b/arch/mn10300/lib/negdi2.c @@ -0,0 +1,57 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public Licence, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public Licence restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GNU CC 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 Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* It is incorrect to include config.h here, because this file is being + compiled for the target, and hence definitions concerning only the host + do not apply. */ + +#include <linux/types.h> + +union DWunion { + s64 ll; + struct { + s32 low; + s32 high; + } s; +}; + +s64 __negdi2(s64 u) +{ + union DWunion w; + union DWunion uu; + + uu.ll = u; + + w.s.low = -uu.s.low; + w.s.high = -uu.s.high - ((u32) w.s.low > 0); + + return w.ll; +} diff --git a/arch/mn10300/lib/usercopy.c b/arch/mn10300/lib/usercopy.c new file mode 100644 index 00000000000..a75b203059c --- /dev/null +++ b/arch/mn10300/lib/usercopy.c @@ -0,0 +1,166 @@ +/* MN10300 Userspace accessor functions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to, from, n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to, from, n); + return n; +} + +/* + * Copy a null terminated string from userspace. + */ +#define __do_strncpy_from_user(dst, src, count, res) \ +do { \ + int w; \ + asm volatile( \ + " mov %1,%0\n" \ + " cmp 0,%1\n" \ + " beq 2f\n" \ + "0:\n" \ + " movbu (%5),%2\n" \ + "1:\n" \ + " movbu %2,(%6)\n" \ + " inc %5\n" \ + " inc %6\n" \ + " cmp 0,%2\n" \ + " beq 2f\n" \ + " add -1,%1\n" \ + " bne 0b\n" \ + "2:\n" \ + " sub %1,%0\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + "4:\n" \ + " mov %3,%0\n" \ + " jmp 3b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 1b,4b\n" \ + " .previous" \ + :"=&r"(res), "=r"(count), "=&r"(w) \ + :"i"(-EFAULT), "1"(count), "a"(src), "a"(dst) \ + :"memory"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Clear a userspace memory + */ +#define __do_clear_user(addr, size) \ +do { \ + int w; \ + asm volatile( \ + " cmp 0,%0\n" \ + " beq 1f\n" \ + " clr %1\n" \ + "0: movbu %1,(%3,%2)\n" \ + " inc %3\n" \ + " cmp %0,%3\n" \ + " bne 0b\n" \ + "1:\n" \ + " sub %3,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous\n" \ + : "+r"(size), "=&r"(w) \ + : "a"(addr), "d"(0) \ + : "memory"); \ +} while (0) + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ +long strnlen_user(const char *s, long n) +{ + unsigned long res, w; + + if (!__addr_ok(s)) + return 0; + + if (n < 0 || n + (u_long) s > current_thread_info()->addr_limit.seg) + n = current_thread_info()->addr_limit.seg - (u_long)s; + + asm volatile( + "0: cmp %4,%0\n" + " beq 2f\n" + "1: movbu (%0,%3),%1\n" + " inc %0\n" + " cmp 0,%1\n" + " beq 3f\n" + " bra 0b\n" + "2: clr %0\n" + "3:\n" + ".section .fixup,\"ax\"\n" + "4: jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 1b,4b\n" + ".previous\n" + :"=d"(res), "=&r"(w) + :"0"(0), "a"(s), "r"(n) + :"memory"); + return res; +} diff --git a/arch/mn10300/mm/Makefile b/arch/mn10300/mm/Makefile new file mode 100644 index 00000000000..28b9d983db0 --- /dev/null +++ b/arch/mn10300/mm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the MN10300-specific memory management code +# + +obj-y := \ + init.o fault.o pgtable.o extable.o tlb-mn10300.o mmu-context.o \ + misalignment.o dma-alloc.o + +ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y) +obj-y += cache.o cache-mn10300.o +ifeq ($(CONFIG_MN10300_CACHE_WBACK),y) +obj-y += cache-flush-mn10300.o +endif +endif diff --git a/arch/mn10300/mm/cache-flush-mn10300.S b/arch/mn10300/mm/cache-flush-mn10300.S new file mode 100644 index 00000000000..c8ed1cbac10 --- /dev/null +++ b/arch/mn10300/mm/cache-flush-mn10300.S @@ -0,0 +1,192 @@ +/* MN10300 CPU core caching routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> + + .am33_2 + .globl mn10300_dcache_flush + .globl mn10300_dcache_flush_page + .globl mn10300_dcache_flush_range + .globl mn10300_dcache_flush_range2 + .globl mn10300_dcache_flush_inv + .globl mn10300_dcache_flush_inv_page + .globl mn10300_dcache_flush_inv_range + .globl mn10300_dcache_flush_inv_range2 + +############################################################################### +# +# void mn10300_dcache_flush(void) +# Flush the entire data cache back to RAM +# +############################################################################### + ALIGN +mn10300_dcache_flush: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_flush_end + + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + +mn10300_dcache_flush_skip: + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_loop + +mn10300_dcache_flush_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_flush_page(unsigned start) +# void mn10300_dcache_flush_range(unsigned start, unsigned end) +# void mn10300_dcache_flush_range2(unsigned start, unsigned size) +# Flush a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_flush_page: + mov PAGE_SIZE,d1 +mn10300_dcache_flush_range2: + add d0,d1 +mn10300_dcache_flush_range: + movm [d2,d3],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_flush_range_end + + # round start addr down + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + # write a request to flush all instances of an address from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + or L1_CACHE_TAG_VALID,a1 # retain valid entries in the + # cache + +mn10300_dcache_flush_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_dcache_flush_range_loop + +mn10300_dcache_flush_range_end: + ret [d2,d3],8 + +############################################################################### +# +# void mn10300_dcache_flush_inv(void) +# Flush the entire data cache and invalidate all entries +# +############################################################################### + ALIGN +mn10300_dcache_flush_inv: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_flush_inv_end + + # hit each line in the dcache with an unconditional purge + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_inv_loop: + mov (a1),d0 # unconditional purge + + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_inv_loop + +mn10300_dcache_flush_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_flush_inv_page(unsigned start) +# void mn10300_dcache_flush_inv_range(unsigned start, unsigned end) +# void mn10300_dcache_flush_inv_range2(unsigned start, unsigned size) +# Flush and invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_flush_inv_page: + mov PAGE_SIZE,d1 +mn10300_dcache_flush_inv_range2: + add d0,d1 +mn10300_dcache_flush_inv_range: + movm [d2,d3],(sp) + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_flush_inv_range_end + + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start + # addr down + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + # write a request to flush and invalidate all instances of an address + # from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + +mn10300_dcache_flush_inv_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # in all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_dcache_flush_inv_range_loop + +mn10300_dcache_flush_inv_range_end: + ret [d2,d3],8 diff --git a/arch/mn10300/mm/cache-mn10300.S b/arch/mn10300/mm/cache-mn10300.S new file mode 100644 index 00000000000..e839d0aedd6 --- /dev/null +++ b/arch/mn10300/mm/cache-mn10300.S @@ -0,0 +1,289 @@ +/* MN10300 CPU core caching routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> + +#define mn10300_dcache_inv_range_intr_interval \ + +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) + +#if mn10300_dcache_inv_range_intr_interval > 0xff +#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less +#endif + + .am33_2 + + .globl mn10300_icache_inv + .globl mn10300_dcache_inv + .globl mn10300_dcache_inv_range + .globl mn10300_dcache_inv_range2 + .globl mn10300_dcache_inv_page + +############################################################################### +# +# void mn10300_icache_inv(void) +# Invalidate the entire icache +# +############################################################################### + ALIGN +mn10300_icache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq mn10300_icache_inv_end + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the icache + and ~CHCTR_ICEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + and ~CHCTR_ICINV,d0 + or CHCTR_ICEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + mov d1,epsw + +mn10300_icache_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_inv(void) +# Invalidate the entire dcache +# +############################################################################### + ALIGN +mn10300_dcache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_inv_end + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the dcache + and ~CHCTR_DCEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # invalidate + or CHCTR_DCINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + and ~CHCTR_DCINV,d0 + or CHCTR_DCEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + mov d1,epsw + +mn10300_dcache_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_inv_range(unsigned start, unsigned end) +# void mn10300_dcache_inv_range2(unsigned start, unsigned size) +# void mn10300_dcache_inv_page(unsigned start) +# Invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_inv_page: + mov PAGE_SIZE,d1 +mn10300_dcache_inv_range2: + add d0,d1 +mn10300_dcache_inv_range: + movm [d2,d3,a2],(sp) + mov CHCTR,a2 + + movhu (a2),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_inv_range_end + + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start + # addr down + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + clr d2 # we're going to clear tag ram + # entries + + # read the tags from the tag RAM, and if they indicate a valid dirty + # cache line then invalidate that line + mov DCACHE_TAG(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache tag RAM + # access address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base + +mn10300_dcache_inv_range_outer_loop: + # disable interrupts + mov epsw,d3 + and ~EPSW_IE,epsw + nop # note that reading CHCTR and + # AND'ing D0 occupy two delay + # slots after disabling + # interrupts + + # disable the dcache + movhu (a2),d0 + and ~CHCTR_DCEN,d0 + movhu d0,(a2) + + # and wait for it to calm down + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + +mn10300_dcache_inv_range_loop: + + # process the way 0 slot + mov (L1_CACHE_WAYDISP*0,a0),d0 # read the tag in the way 0 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_0 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_0 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_0: + + # process the way 1 slot + mov (L1_CACHE_WAYDISP*1,a0),d0 # read the tag in the way 1 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_1 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_1 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_1: + + # process the way 2 slot + mov (L1_CACHE_WAYDISP*2,a0),d0 # read the tag in the way 2 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_2 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_2 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_2: + + # process the way 3 slot + mov (L1_CACHE_WAYDISP*3,a0),d0 # read the tag in the way 3 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_3 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_3 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_3: + + # approx every N steps we re-enable the cache and see if there are any + # interrupts to be processed + # we also break out if we've reached the end of the loop + # (the bottom nibble of the count is zero in both cases) + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + btst mn10300_dcache_inv_range_intr_interval,d1 + bne mn10300_dcache_inv_range_loop + + # wait for the cache to finish what it's doing + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + or CHCTR_DCEN,d0 + movhu d0,(a2) + movhu (a2),d0 + + # re-enable interrupts + # - we don't bother with delay NOPs as we'll have enough instructions + # before we disable interrupts again to give the interrupts a chance + # to happen + mov d3,epsw + + # go around again if the counter hasn't yet reached zero + add 0,d1 + bne mn10300_dcache_inv_range_outer_loop + +mn10300_dcache_inv_range_end: + ret [d2,d3,a2],12 diff --git a/arch/mn10300/mm/cache.c b/arch/mn10300/mm/cache.c new file mode 100644 index 00000000000..1b76719ec1c --- /dev/null +++ b/arch/mn10300/mm/cache.c @@ -0,0 +1,121 @@ +/* MN10300 Cache flushing routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/threads.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +EXPORT_SYMBOL(mn10300_icache_inv); +EXPORT_SYMBOL(mn10300_dcache_inv); +EXPORT_SYMBOL(mn10300_dcache_inv_range); +EXPORT_SYMBOL(mn10300_dcache_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_inv_page); + +#ifdef CONFIG_MN10300_CACHE_WBACK +EXPORT_SYMBOL(mn10300_dcache_flush); +EXPORT_SYMBOL(mn10300_dcache_flush_inv); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_page); +EXPORT_SYMBOL(mn10300_dcache_flush_range); +EXPORT_SYMBOL(mn10300_dcache_flush_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_page); +#endif + +/* + * write a page back from the dcache and invalidate the icache so that we can + * run code from it that we've just written into it + */ +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + mn10300_dcache_flush_page(page_to_phys(page)); + mn10300_icache_inv(); +} +EXPORT_SYMBOL(flush_icache_page); + +/* + * write some code we've just written back from the dcache and invalidate the + * icache so that we can run that code + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ +#ifdef CONFIG_MN10300_CACHE_WBACK + unsigned long addr, size, off; + struct page *page; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ppte, pte; + + for (; start < end; start += size) { + /* work out how much of the page to flush */ + off = start & (PAGE_SIZE - 1); + + size = end - start; + if (size > PAGE_SIZE - off) + size = PAGE_SIZE - off; + + /* get the physical address the page is mapped to from the page + * tables */ + pgd = pgd_offset(current->mm, start); + if (!pgd || !pgd_val(*pgd)) + continue; + + pud = pud_offset(pgd, start); + if (!pud || !pud_val(*pud)) + continue; + + pmd = pmd_offset(pud, start); + if (!pmd || !pmd_val(*pmd)) + continue; + + ppte = pte_offset_map(pmd, start); + if (!ppte) + continue; + pte = *ppte; + pte_unmap(ppte); + + if (pte_none(pte)) + continue; + + page = pte_page(pte); + if (!page) + continue; + + addr = page_to_phys(page); + + /* flush the dcache and invalidate the icache coverage on that + * region */ + mn10300_dcache_flush_range2(addr + off, size); + } +#endif + + mn10300_icache_inv(); +} +EXPORT_SYMBOL(flush_icache_range); + +/* + * allow userspace to flush the instruction cache + */ +asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) +{ + if (end < start) + return -EINVAL; + + flush_icache_range(start, end); + return 0; +} diff --git a/arch/mn10300/mm/dma-alloc.c b/arch/mn10300/mm/dma-alloc.c new file mode 100644 index 00000000000..f3649d8f50e --- /dev/null +++ b/arch/mn10300/mm/dma-alloc.c @@ -0,0 +1,56 @@ +/* MN10300 Dynamic DMA mapping support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-dma.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <asm/io.h> + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + unsigned long addr; + void *ret; + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || dev->coherent_dma_mask < 0xffffffff) + gfp |= GFP_DMA; + + addr = __get_free_pages(gfp, get_order(size)); + if (!addr) + return NULL; + + /* map the coherent memory through the uncached memory window */ + ret = (void *) (addr | 0x20000000); + + /* fill the memory with obvious rubbish */ + memset((void *) addr, 0xfb, size); + + /* write back and evict all cache lines covering this region */ + mn10300_dcache_flush_inv_range2(virt_to_phys((void *) addr), PAGE_SIZE); + + *dma_handle = virt_to_bus((void *) addr); + return ret; +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + unsigned long addr = (unsigned long) vaddr & ~0x20000000; + + free_pages(addr, get_order(size)); +} +EXPORT_SYMBOL(dma_free_coherent); diff --git a/arch/mn10300/mm/extable.c b/arch/mn10300/mm/extable.c new file mode 100644 index 00000000000..25e5485ab87 --- /dev/null +++ b/arch/mn10300/mm/extable.c @@ -0,0 +1,26 @@ +/* MN10300 In-kernel exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/uaccess.h> + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->pc); + if (fixup) { + regs->pc = fixup->fixup; + return 1; + } + + return 0; +} diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c new file mode 100644 index 00000000000..78f092ca031 --- /dev/null +++ b/arch/mn10300/mm/fault.c @@ -0,0 +1,405 @@ +/* MN10300 MMU Fault handler + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/vt_kern.h> /* For unblank_screen() */ + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgalloc.h> +#include <asm/hardirq.h> +#include <asm/gdb-stub.h> +#include <asm/cpu-regs.h> + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + /* Many serial drivers do __global_cli() */ + global_irq_lock = 0; +#endif + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console + * up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); +} + +#if 0 +static void print_pagetable_entries(pgd_t *pgdir, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgdir + __pgd_offset(address); + printk(KERN_DEBUG "pgd entry %p: %016Lx\n", + pgd, (long long) pgd_val(*pgd)); + + if (!pgd_present(*pgd)) { + printk(KERN_DEBUG "... pgd not present!\n"); + return; + } + pmd = pmd_offset(pgd, address); + printk(KERN_DEBUG "pmd entry %p: %016Lx\n", + pmd, (long long)pmd_val(*pmd)); + + if (!pmd_present(*pmd)) { + printk(KERN_DEBUG "... pmd not present!\n"); + return; + } + pte = pte_offset(pmd, address); + printk(KERN_DEBUG "pte entry %p: %016Lx\n", + pte, (long long) pte_val(*pte)); + + if (!pte_present(*pte)) + printk(KERN_DEBUG "... pte not present!\n"); +} +#endif + +asmlinkage void monitor_signal(struct pt_regs *); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * fault_code: + * - LSW: either MMUFCR_IFC or MMUFCR_DFC as appropriate + * - MSW: 0 if data access, 1 if instruction access + * - bit 0: TLB miss flag + * - bit 1: initial write + * - bit 2: page invalid + * - bit 3: protection violation + * - bit 4: accessor (0=user 1=kernel) + * - bit 5: 0=read 1=write + * - bit 6-8: page protection spec + * - bit 9: illegal address + * - bit 16: 0=data 1=ins + * + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code, + unsigned long address) +{ + struct vm_area_struct *vma; + struct task_struct *tsk; + struct mm_struct *mm; + unsigned long page; + siginfo_t info; + int write, fault; + +#ifdef CONFIG_GDBSTUB + /* handle GDB stub causing a fault */ + if (gdbstub_busy) { + gdbstub_exception(regs, TBR & TBR_INT_CODE); + return; + } +#endif + +#if 0 + printk(KERN_DEBUG "--- do_page_fault(%p,%s:%04lx,%08lx)\n", + regs, + fault_code & 0x10000 ? "ins" : "data", + fault_code & 0xffff, address); +#endif + + tsk = current; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * This verifies that the fault happens in kernel space + * and that the fault was a page not present (invalid) error + */ + if (address >= VMALLOC_START && address < VMALLOC_END && + (fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR && + (fault_code & MMUFCR_xFC_PGINVAL) == MMUFCR_xFC_PGINVAL + ) + goto vmalloc_fault; + + mm = tsk->mm; + info.si_code = SEGV_MAPERR; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || !mm) + goto no_context; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + /* accessing the stack below the stack pointer is always a + * bug */ + if ((address & PAGE_MASK) + 2 * PAGE_SIZE < regs->sp) { +#if 0 + printk(KERN_WARNING + "[%d] ### Access below stack @%lx (sp=%lx)\n", + current->pid, address, regs->sp); + printk(KERN_WARNING + "vma [%08x - %08x]\n", + vma->vm_start, vma->vm_end); + show_registers(regs); + printk(KERN_WARNING + "[%d] ### Code: [%08lx]" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + current->pid, + regs->pc, + ((u8 *) regs->pc)[0], + ((u8 *) regs->pc)[1], + ((u8 *) regs->pc)[2], + ((u8 *) regs->pc)[3], + ((u8 *) regs->pc)[4], + ((u8 *) regs->pc)[5], + ((u8 *) regs->pc)[6], + ((u8 *) regs->pc)[7] + ); +#endif + goto bad_area; + } + } + + if (expand_stack(vma, address)) + goto bad_area; + +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (fault_code & (MMUFCR_xFC_PGINVAL|MMUFCR_xFC_TYPE)) { + default: /* 3: write, present */ + case MMUFCR_xFC_TYPE_WRITE: +#ifdef TEST_VERIFY_AREA + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + printk(KERN_DEBUG "WP fault at %08lx\n", regs->pc); +#endif + /* write to absent page */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_WRITE: + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + + /* read from protected page */ + case MMUFCR_xFC_TYPE_READ: + goto bad_area; + + /* read from absent page present */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_READ: + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + break; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); + } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + monitor_signal(regs); + + /* User mode accesses just cause a SIGSEGV */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + monitor_signal(regs); + /* Are we prepared to handle this kernel fault? */ + if (fixup_exception(regs)) + return; + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT + "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT + "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n", address); + printk(" printing pc:\n"); + printk(KERN_ALERT "%08lx\n", regs->pc); + +#ifdef CONFIG_GDBSTUB + gdbstub_intercept( + regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR); +#endif + + page = PTBR; + page = ((unsigned long *) __va(page))[address >> 22]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & 1) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + + die("Oops", regs, fault_code); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + monitor_signal(regs); + printk(KERN_ALERT "VM: killing process %s\n", tsk->comm); + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + monitor_signal(regs); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + goto no_context; + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. + */ + int index = pgd_index(address); + pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + pgd_k = init_mm.pgd + index; + + if (!pgd_present(*pgd_k)) + goto no_context; + + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto no_context; + + pmd_k = pmd_offset(pud_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + + pgd = (pgd_t *) PTBR + index; + pud = pud_offset(pgd, address); + pmd = pmd_offset(pud, address); + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + return; + } +} diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c new file mode 100644 index 00000000000..8c5d88c7b90 --- /dev/null +++ b/arch/mn10300/mm/init.c @@ -0,0 +1,160 @@ +/* MN10300 Memory management initialisation + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/bootmem.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/dma.h> +#include <asm/tlb.h> +#include <asm/sections.h> + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +unsigned long highstart_pfn, highend_pfn; + +/* + * set up paging + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0,}; + pte_t *ppte; + int loop; + + /* main kernel space -> RAM mapping is handled as 1:1 transparent by + * the MMU */ + memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); + memset(kernel_vmalloc_ptes, 0, sizeof(kernel_vmalloc_ptes)); + + /* load the VMALLOC area PTE table addresses into the kernel PGD */ + ppte = kernel_vmalloc_ptes; + for (loop = VMALLOC_START / (PAGE_SIZE * PTRS_PER_PTE); + loop < VMALLOC_END / (PAGE_SIZE * PTRS_PER_PTE); + loop++ + ) { + set_pgd(swapper_pg_dir + loop, __pgd(__pa(ppte) | _PAGE_TABLE)); + ppte += PAGE_SIZE / sizeof(pte_t); + } + + /* declare the sizes of the RAM zones (only use the normal zone) */ + zones_size[ZONE_NORMAL] = + (contig_page_data.bdata->node_low_pfn) - + (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT); + + /* pass the memory from the bootmem allocator to the main allocator */ + free_area_init(zones_size); + + __flush_tlb_all(); +} + +/* + * transfer all the memory from the bootmem allocator to the runtime allocator + */ +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + if (!mem_map) + BUG(); + +#define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) + + max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; + high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + reservedpages = 0; + for (tmp = 0; tmp < num_physpages; tmp++) + if (PageReserved(&mem_map[tmp])) + reservedpages++; + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk(KERN_INFO + "Memory: %luk/%luk available" + " (%dk kernel code, %dk reserved, %dk data, %dk init," + " %ldk highmem)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT - 10), + max_mapnr << (PAGE_SHIFT - 10), + codesize >> 10, + reservedpages << (PAGE_SHIFT - 10), + datasize >> 10, + initsize >> 10, + (unsigned long) (totalhigh_pages << (PAGE_SHIFT - 10)) + ); +} + +/* + * + */ +void free_init_pages(char *what, unsigned long begin, unsigned long end) +{ + unsigned long addr; + + for (addr = begin; addr < end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + memset((void *) addr, 0xcc, PAGE_SIZE); + free_page(addr); + totalram_pages++; + } + printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); +} + +/* + * recycle memory containing stuff only required for initialisation + */ +void free_initmem(void) +{ + free_init_pages("unused kernel memory", + (unsigned long) &__init_begin, + (unsigned long) &__init_end); +} + +/* + * dispose of the memory on which the initial ramdisk resided + */ +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + free_init_pages("initrd memory", start, end); +} +#endif diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c new file mode 100644 index 00000000000..32aa89dc384 --- /dev/null +++ b/arch/mn10300/mm/misalignment.c @@ -0,0 +1,661 @@ +/* MN10300 Misalignment fixup handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <asm/fpu.h> +#include <asm/gdb-stub.h> +#include <asm/asm-offsets.h> + +#if 0 +#define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__) +#else +#define kdebug(FMT, ...) do {} while (0) +#endif + +static int misalignment_addr(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + void **_address, unsigned long **_postinc); + +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + unsigned long **_register); + +static inline unsigned int_log2(unsigned x) +{ + unsigned y; + asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0)); + return y; +} +#define log2(x) int_log2(x) + +static const unsigned Dreg_index[] = { + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +static const unsigned Areg_index[] = { + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2 +}; + +static const unsigned Rreg_index[] = { + REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2, + REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2, + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2, + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +enum format_id { + FMT_S0, + FMT_S1, + FMT_S2, + FMT_S4, + FMT_D0, + FMT_D1, + FMT_D2, + FMT_D4, + FMT_D6, + FMT_D7, + FMT_D8, + FMT_D9, +}; + +struct { + u_int8_t opsz, dispsz; +} format_tbl[16] = { + [FMT_S0] = { 8, 0 }, + [FMT_S1] = { 8, 8 }, + [FMT_S2] = { 8, 16 }, + [FMT_S4] = { 8, 32 }, + [FMT_D0] = { 16, 0 }, + [FMT_D1] = { 16, 8 }, + [FMT_D2] = { 16, 16 }, + [FMT_D4] = { 16, 32 }, + [FMT_D6] = { 24, 0 }, + [FMT_D7] = { 24, 8 }, + [FMT_D8] = { 24, 24 }, + [FMT_D9] = { 24, 32 }, +}; + +enum value_id { + DM0, /* data reg in opcode in bits 0-1 */ + DM1, /* data reg in opcode in bits 2-3 */ + DM2, /* data reg in opcode in bits 4-5 */ + AM0, /* addr reg in opcode in bits 0-1 */ + AM1, /* addr reg in opcode in bits 2-3 */ + AM2, /* addr reg in opcode in bits 4-5 */ + RM0, /* reg in opcode in bits 0-3 */ + RM1, /* reg in opcode in bits 2-5 */ + RM2, /* reg in opcode in bits 4-7 */ + RM4, /* reg in opcode in bits 8-11 */ + RM6, /* reg in opcode in bits 12-15 */ + + RD0, /* reg in displacement in bits 0-3 */ + RD2, /* reg in displacement in bits 4-7 */ + + SP, /* stack pointer */ + + SD8, /* 8-bit signed displacement */ + SD16, /* 16-bit signed displacement */ + SD24, /* 24-bit signed displacement */ + SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */ + SIMM8, /* 8-bit signed immediate */ + IMM24, /* 24-bit unsigned immediate */ + IMM32, /* 32-bit unsigned immediate */ + IMM32_HIGH8, /* 32-bit unsigned immediate, high 8-bits in opcode */ + + DN0 = DM0, + DN1 = DM1, + DN2 = DM2, + AN0 = AM0, + AN1 = AM1, + AN2 = AM2, + RN0 = RM0, + RN1 = RM1, + RN2 = RM2, + RN4 = RM4, + RN6 = RM6, + DI = DM1, + RI = RM2, + +}; + +struct mn10300_opcode { + const char *name; + u_int32_t opcode; + u_int32_t opmask; + unsigned exclusion; + + enum format_id format; + + unsigned cpu_mask; +#define AM33 330 + + unsigned params[2]; +#define MEM(ADDR) (0x80000000 | (ADDR)) +#define MEM2(ADDR1, ADDR2) (0x80000000 | (ADDR1) << 8 | (ADDR2)) +#define MEMINC(ADDR) (0x81000000 | (ADDR)) +#define MEMINC2(ADDR, INC) (0x81000000 | (ADDR) << 8 | (INC)) +}; + +/* LIBOPCODES EXCERPT + Assemble Matsushita MN10300 instructions. + Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public Licence as published by + the Free Software Foundation; either version 2 of the Licence, or + (at your option) any later version. + + 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 Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +static const struct mn10300_opcode mn10300_opcodes[] = { +{ "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}}, +{ "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}}, +{ "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}}, +{ "mov", 0xf010, 0xfff0, 0, FMT_D0, 0, {AM1, MEM(AN0)}}, +{ "mov", 0xf300, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "mov", 0xf340, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "mov", 0xf380, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), AN2}}, +{ "mov", 0xf3c0, 0xffc0, 0, FMT_D0, 0, {AM2, MEM2(DI, AN0)}}, +{ "mov", 0xf80000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}}, +{ "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf8f000, 0xfffc00, 0, FMT_D1, AM33, {MEM2(SD8, AM0), SP}}, +{ "mov", 0xf8f400, 0xfffc00, 0, FMT_D1, AM33, {SP, MEM2(SD8, AN0)}}, +{ "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "mov", 0xf97a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "mov", 0xfa000000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}}, +{ "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}}, +{ "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, + +{ "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}}, +{ "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}}, +{ "movhu", 0xf480, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, +{ 0, 0, 0, 0, 0, 0, {0}}, +}; + +/* + * fix up misalignment problems where possible + */ +asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) +{ + const struct exception_table_entry *fixup; + const struct mn10300_opcode *pop; + unsigned long *registers = (unsigned long *) regs; + unsigned long data, *store, *postinc; + mm_segment_t seg; + siginfo_t info; + uint32_t opcode, disp, noc, xo, xm; + uint8_t *pc, byte; + void *address; + unsigned tmp, npop; + + kdebug("MISALIGN at %lx\n", regs->pc); + + if (in_interrupt()) + die("Misalignment trap in interrupt context", regs, code); + + if (regs->epsw & EPSW_IE) + asm volatile("or %0,epsw" : : "i"(EPSW_IE)); + + seg = get_fs(); + set_fs(KERNEL_DS); + + fixup = search_exception_tables(regs->pc); + + /* first thing to do is to match the opcode */ + pc = (u_int8_t *) regs->pc; + + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = byte; + noc = 8; + + for (pop = mn10300_opcodes; pop->name; pop++) { + npop = log2(pop->opcode | pop->opmask); + if (npop <= 0 || npop > 31) + continue; + npop = (npop + 8) & ~7; + + got_more_bits: + if (npop == noc) { + if ((opcode & pop->opmask) == pop->opcode) + goto found_opcode; + } else if (npop > noc) { + xo = pop->opcode >> (npop - noc); + xm = pop->opmask >> (npop - noc); + + if ((opcode & xm) != xo) + continue; + + /* we've got a partial match (an exact match on the + * first N bytes), so we need to get some more data */ + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = opcode << 8 | byte; + noc += 8; + goto got_more_bits; + } else { + /* there's already been a partial match as long as the + * complete match we're now considering, so this one + * should't match */ + continue; + } + } + + /* didn't manage to find a fixup */ + if (!user_mode(regs)) + printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", + regs->pc, opcode); + +failed: + set_fs(seg); + if (die_if_no_fixup("misalignment error", regs, code)) + return; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRALN; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGBUS, &info, current); + return; + + /* error reading opcodes */ +fetch_error: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %p: fault whilst reading instruction data\n", + pc); + goto failed; + +bad_addr_mode: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported addressing mode %x\n", + regs->pc, opcode); + goto failed; + +bad_reg_mode: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported register mode %x\n", + regs->pc, opcode); + goto failed; + +unsupported_instruction: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported instruction %x (%s)\n", + regs->pc, opcode, pop->name); + goto failed; + +transfer_failed: + set_fs(seg); + if (fixup) { + regs->pc = fixup->fixup; + return; + } + if (die_if_no_fixup("misalignment fixup", regs, code)) + return; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = 0; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGSEGV, &info, current); + return; + + /* we matched the opcode */ +found_opcode: + kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n", + regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]); + + tmp = format_tbl[pop->format].opsz; + if (tmp > noc) + BUG(); /* match was less complete than it ought to have been */ + + if (tmp < noc) { + tmp = noc - tmp; + opcode >>= tmp; + pc -= tmp >> 3; + } + + /* grab the extra displacement (note it's LSB first) */ + disp = 0; + tmp = format_tbl[pop->format].dispsz >> 3; + while (tmp > 0) { + tmp--; + disp <<= 8; + + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + disp |= byte; + } + + set_fs(KERNEL_XDS); + if (fixup || regs->epsw & EPSW_nSL) + set_fs(seg); + + tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000; + if (!tmp) { + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx:" + " insn not move to/from memory %x\n", + regs->pc, opcode); + goto failed; + } + + if (pop->params[0] & 0x80000000) { + /* move memory to register */ + if (!misalignment_addr(registers, pop->params[0], opcode, disp, + &address, &postinc)) + goto bad_addr_mode; + + if (!misalignment_reg(registers, pop->params[1], opcode, disp, + &store)) + goto bad_reg_mode; + + if (strcmp(pop->name, "mov") == 0) { + kdebug("FIXUP: mov (%p),DARn\n", address); + if (copy_from_user(&data, (void *) address, 4) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) + *postinc += 4; + } else if (strcmp(pop->name, "movhu") == 0) { + kdebug("FIXUP: movhu (%p),DARn\n", address); + data = 0; + if (copy_from_user(&data, (void *) address, 2) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) + *postinc += 2; + } else { + goto unsupported_instruction; + } + + *store = data; + } else { + /* move register to memory */ + if (!misalignment_reg(registers, pop->params[0], opcode, disp, + &store)) + goto bad_reg_mode; + + if (!misalignment_addr(registers, pop->params[1], opcode, disp, + &address, &postinc)) + goto bad_addr_mode; + + data = *store; + + if (strcmp(pop->name, "mov") == 0) { + kdebug("FIXUP: mov %lx,(%p)\n", data, address); + if (copy_to_user((void *) address, &data, 4) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += 4; + } else if (strcmp(pop->name, "movhu") == 0) { + kdebug("FIXUP: movhu %hx,(%p)\n", + (uint16_t) data, address); + if (copy_to_user((void *) address, &data, 2) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += 2; + } else { + goto unsupported_instruction; + } + } + + tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; + regs->pc += tmp >> 3; + + set_fs(seg); + return; +} + +/* + * determine the address that was being accessed + */ +static int misalignment_addr(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + void **_address, unsigned long **_postinc) +{ + unsigned long *postinc = NULL, address = 0, tmp; + + params &= 0x7fffffff; + + do { + switch (params & 0xff) { + case DM0: + postinc = ®isters[Dreg_index[opcode & 0x03]]; + address += *postinc; + break; + case DM1: + postinc = ®isters[Dreg_index[opcode >> 2 & 0x0c]]; + address += *postinc; + break; + case DM2: + postinc = ®isters[Dreg_index[opcode >> 4 & 0x30]]; + address += *postinc; + break; + case AM0: + postinc = ®isters[Areg_index[opcode & 0x03]]; + address += *postinc; + break; + case AM1: + postinc = ®isters[Areg_index[opcode >> 2 & 0x0c]]; + address += *postinc; + break; + case AM2: + postinc = ®isters[Areg_index[opcode >> 4 & 0x30]]; + address += *postinc; + break; + case RM0: + postinc = ®isters[Rreg_index[opcode & 0x0f]]; + address += *postinc; + break; + case RM1: + postinc = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + address += *postinc; + break; + case RM2: + postinc = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + address += *postinc; + break; + case RM4: + postinc = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + address += *postinc; + break; + case RM6: + postinc = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + address += *postinc; + break; + case RD0: + postinc = ®isters[Rreg_index[disp & 0x0f]]; + address += *postinc; + break; + case RD2: + postinc = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + address += *postinc; + break; + + case SD8: + case SIMM8: + address += (int32_t) (int8_t) (disp & 0xff); + break; + case SD16: + address += (int32_t) (int16_t) (disp & 0xffff); + break; + case SD24: + tmp = disp << 8; + asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); + address += tmp; + break; + case SIMM4_2: + tmp = opcode >> 4 & 0x0f; + tmp <<= 28; + asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); + address += tmp; + break; + case IMM24: + address += disp & 0x00ffffff; + break; + case IMM32: + case IMM32_HIGH8: + address += disp; + break; + default: + return 0; + } + } while ((params >>= 8)); + + *_address = (void *) address; + *_postinc = postinc; + return 1; +} + +/* + * determine the register that is acting as source/dest + */ +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + unsigned long **_register) +{ + params &= 0x7fffffff; + + if (params & 0xffffff00) + return 0; + + switch (params & 0xff) { + case DM0: + *_register = ®isters[Dreg_index[opcode & 0x03]]; + break; + case DM1: + *_register = ®isters[Dreg_index[opcode >> 2 & 0x03]]; + break; + case DM2: + *_register = ®isters[Dreg_index[opcode >> 4 & 0x03]]; + break; + case AM0: + *_register = ®isters[Areg_index[opcode & 0x03]]; + break; + case AM1: + *_register = ®isters[Areg_index[opcode >> 2 & 0x03]]; + break; + case AM2: + *_register = ®isters[Areg_index[opcode >> 4 & 0x03]]; + break; + case RM0: + *_register = ®isters[Rreg_index[opcode & 0x0f]]; + break; + case RM1: + *_register = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + break; + case RM2: + *_register = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + break; + case RM4: + *_register = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + break; + case RM6: + *_register = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + break; + case RD0: + *_register = ®isters[Rreg_index[disp & 0x0f]]; + break; + case RD2: + *_register = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + break; + case SP: + *_register = ®isters[REG_SP >> 2]; + break; + + default: + return 0; + } + + return 1; +} diff --git a/arch/mn10300/mm/mmu-context.c b/arch/mn10300/mm/mmu-context.c new file mode 100644 index 00000000000..31c9d27a75a --- /dev/null +++ b/arch/mn10300/mm/mmu-context.c @@ -0,0 +1,80 @@ +/* MN10300 MMU context allocation and management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/mmu_context.h> +#include <asm/tlbflush.h> + +/* + * list of the MMU contexts last allocated on each CPU + */ +unsigned long mmu_context_cache[NR_CPUS] = { + [0 ... NR_CPUS - 1] = MMU_CONTEXT_FIRST_VERSION * 2 - 1, +}; + +/* + * flush the specified TLB entry + */ +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) +{ + unsigned long pteu, cnx, flags; + + addr &= PAGE_MASK; + + /* make sure the context doesn't migrate and defend against + * interference from vmalloc'd regions */ + local_irq_save(flags); + + cnx = mm_context(vma->vm_mm); + + if (cnx != MMU_NO_CONTEXT) { + pteu = addr | (cnx & 0x000000ffUL); + IPTEU = pteu; + DPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = 0; + if (DPTEL & xPTEL_V) + DPTEL = 0; + } + + local_irq_restore(flags); +} + +/* + * preemptively set a TLB entry + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) +{ + unsigned long pteu, ptel, cnx, flags; + + addr &= PAGE_MASK; + ptel = pte_val(pte) & ~(xPTEL_UNUSED1 | xPTEL_UNUSED2); + + /* make sure the context doesn't migrate and defend against + * interference from vmalloc'd regions */ + local_irq_save(flags); + + cnx = mm_context(vma->vm_mm); + + if (cnx != MMU_NO_CONTEXT) { + pteu = addr | (cnx & 0x000000ffUL); + if (!(pte_val(pte) & _PAGE_NX)) { + IPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = ptel; + } + DPTEU = pteu; + if (DPTEL & xPTEL_V) + DPTEL = ptel; + } + + local_irq_restore(flags); +} diff --git a/arch/mn10300/mm/pgtable.c b/arch/mn10300/mm/pgtable.c new file mode 100644 index 00000000000..a477038752b --- /dev/null +++ b/arch/mn10300/mm/pgtable.c @@ -0,0 +1,197 @@ +/* MN10300 Page table management + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/highmem.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/spinlock.h> +#include <linux/quicklist.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/tlb.h> +#include <asm/tlbflush.h> + +void show_mem(void) +{ + unsigned long i; + int free = 0, total = 0, reserved = 0, shared = 0; + + int cached = 0; + printk(KERN_INFO "Mem-info:\n"); + show_free_areas(); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map + i)) + reserved++; + else if (PageSwapCache(mem_map + i)) + cached++; + else if (!page_count(mem_map + i)) + free++; + else + shared += page_count(mem_map + i) - 1; + } + printk(KERN_INFO "%d pages of RAM\n", total); + printk(KERN_INFO "%d free pages\n", free); + printk(KERN_INFO "%d reserved pages\n", reserved); + printk(KERN_INFO "%d pages shared\n", shared); + printk(KERN_INFO "%d pages swap cached\n", cached); +} + +/* + * Associate a large virtual page frame with a given physical page frame + * and protection flags for that frame. pfn is for the base of the page, + * vaddr is what the page gets mapped to - both must be properly aligned. + * The pmd must already be instantiated. Assumes PAE mode. + */ +void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + + if (vaddr & (PMD_SIZE-1)) { /* vaddr is misaligned */ + printk(KERN_ERR "set_pmd_pfn: vaddr misaligned\n"); + return; /* BUG(); */ + } + if (pfn & (PTRS_PER_PTE-1)) { /* pfn is misaligned */ + printk(KERN_ERR "set_pmd_pfn: pfn misaligned\n"); + return; /* BUG(); */ + } + pgd = swapper_pg_dir + pgd_index(vaddr); + if (pgd_none(*pgd)) { + printk(KERN_ERR "set_pmd_pfn: pgd_none\n"); + return; /* BUG(); */ + } + pud = pud_offset(pgd, vaddr); + pmd = pmd_offset(pud, vaddr); + set_pmd(pmd, pfn_pmd(pfn, flags)); + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); + if (pte) + clear_page(pte); + return pte; +} + +struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *pte; + +#ifdef CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0); +#else + pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); +#endif + if (pte) + clear_highpage(pte); + return pte; +} + +/* + * List of all pgd's needed for non-PAE so it can invalidate entries + * in both cached and uncached pgd's; not needed for PAE since the + * kernel pmd is shared. If PAE were not to share the pmd a similar + * tactic would be needed. This is essentially codepath-based locking + * against pageattr.c; it is the unique case in which a valid change + * of kernel pagetables can't be lazily synchronized by vmalloc faults. + * vmalloc faults work because attached pagetables are never freed. + * If the locking proves to be non-performant, a ticketing scheme with + * checks at dup_mmap(), exec(), and other mmlist addition points + * could be used. The locking scheme was chosen on the basis of + * manfred's recommendations and having no core impact whatsoever. + * -- wli + */ +DEFINE_SPINLOCK(pgd_lock); +struct page *pgd_list; + +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + page->index = (unsigned long) pgd_list; + if (pgd_list) + set_page_private(pgd_list, (unsigned long) &page->index); + pgd_list = page; + set_page_private(page, (unsigned long) &pgd_list); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *next, **pprev, *page = virt_to_page(pgd); + next = (struct page *) page->index; + pprev = (struct page **) page_private(page); + *pprev = next; + if (next) + set_page_private(next, (unsigned long) pprev); +} + +void pgd_ctor(void *pgd) +{ + unsigned long flags; + + if (PTRS_PER_PMD == 1) + spin_lock_irqsave(&pgd_lock, flags); + + memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + + if (PTRS_PER_PMD > 1) + return; + + pgd_list_add(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); + memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); +} + +/* never called when PTRS_PER_PMD > 1 */ +void pgd_dtor(void *pgd) +{ + unsigned long flags; /* can be called from interrupt context */ + + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); +} + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + return quicklist_alloc(0, GFP_KERNEL, pgd_ctor); +} + +void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + quicklist_free(0, pgd_dtor, pgd); +} + +void __init pgtable_cache_init(void) +{ +} + +void check_pgt_cache(void) +{ + quicklist_trim(0, pgd_dtor, 25, 16); +} diff --git a/arch/mn10300/mm/tlb-mn10300.S b/arch/mn10300/mm/tlb-mn10300.S new file mode 100644 index 00000000000..789208094e9 --- /dev/null +++ b/arch/mn10300/mm/tlb-mn10300.S @@ -0,0 +1,207 @@ +############################################################################### +# +# TLB loading functions +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/frame.inc> +#include <asm/page.h> +#include <asm/pgtable.h> + +############################################################################### +# +# Instruction TLB Miss handler entry point +# +############################################################################### + .type itlb_miss,@function +ENTRY(itlb_miss) + and ~EPSW_NMID,epsw +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + + mov (IPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq itlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq itlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) + bset _PAGE_ACCESSED,(0,a2) + and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 +itlb_miss_set: + mov d2,(IPTEL) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +itlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra itlb_miss_set + + .size itlb_miss, . - itlb_miss + +############################################################################### +# +# Data TLB Miss handler entry point +# +############################################################################### + .type dtlb_miss,@function +ENTRY(dtlb_miss) + and ~EPSW_NMID,epsw +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + + mov (DPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq dtlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq dtlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) + bset _PAGE_ACCESSED,(0,a2) + and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 +dtlb_miss_set: + mov d2,(DPTEL) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +dtlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra dtlb_miss_set + .size dtlb_miss, . - dtlb_miss + +############################################################################### +# +# Instruction TLB Address Error handler entry point +# +############################################################################### + .type itlb_aerror,@function +ENTRY(itlb_aerror) + and ~EPSW_NMID,epsw + add -4,sp + SAVE_ALL + add -4,sp # need to pass three params + + # calculate the fault code + movhu (MMUFCR_IFC),d1 + or 0x00010000,d1 # it's an instruction fetch + + # determine the page address + mov (IPTEU),a2 + mov a2,d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(IPTEL) + + and ~EPSW_NMID,epsw + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size itlb_aerror, . - itlb_aerror + +############################################################################### +# +# Data TLB Address Error handler entry point +# +############################################################################### + .type dtlb_aerror,@function +ENTRY(dtlb_aerror) + and ~EPSW_NMID,epsw + add -4,sp + mov d1,(sp) + + movhu (MMUFCR_DFC),d1 # is it the initial valid write + # to this page? + and MMUFCR_xFC_INITWR,d1 + beq dtlb_pagefault # jump if not + + mov (DPTEL),d1 # set the dirty bit + # (don't replace with BSET!) + or _PAGE_DIRTY,d1 + mov d1,(DPTEL) + mov (sp),d1 + add 4,sp + rti + + ALIGN +dtlb_pagefault: + mov (sp),d1 + SAVE_ALL + add -4,sp # need to pass three params + + # calculate the fault code + movhu (MMUFCR_DFC),d1 + + # determine the page address + mov (DPTEU),a2 + mov a2,d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(DPTEL) + + and ~EPSW_NMID,epsw + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size dtlb_aerror, . - dtlb_aerror diff --git a/arch/mn10300/oprofile/Kconfig b/arch/mn10300/oprofile/Kconfig new file mode 100644 index 00000000000..19d37730b66 --- /dev/null +++ b/arch/mn10300/oprofile/Kconfig @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff --git a/arch/mn10300/oprofile/Makefile b/arch/mn10300/oprofile/Makefile new file mode 100644 index 00000000000..918dbe60ebb --- /dev/null +++ b/arch/mn10300/oprofile/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the MN10300-specific profiling code +# +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) op_model_null.o + diff --git a/arch/mn10300/oprofile/op_model_null.c b/arch/mn10300/oprofile/op_model_null.c new file mode 100644 index 00000000000..cd4ab374bc4 --- /dev/null +++ b/arch/mn10300/oprofile/op_model_null.c @@ -0,0 +1,22 @@ +/* Null profiling driver + * + * Copyright (C) 2003 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * Licence. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/kernel.h> +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/errno.h> + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + return -ENODEV; +} + +void oprofile_arch_exit(void) +{ +} + diff --git a/arch/mn10300/proc-mn103e010/Makefile b/arch/mn10300/proc-mn103e010/Makefile new file mode 100644 index 00000000000..ac2c9784cd2 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the MN103E010 processor chip specific code +# +obj-y := proc-init.o + diff --git a/arch/mn10300/proc-mn103e010/proc-init.c b/arch/mn10300/proc-mn103e010/proc-init.c new file mode 100644 index 00000000000..9a482efafa8 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/proc-init.c @@ -0,0 +1,75 @@ +/* MN103E010 Processor initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <asm/rtc.h> + +/* + * initialise the on-silicon processor peripherals + */ +asmlinkage void __init processor_init(void) +{ + int loop; + + /* set up the exception table first */ + for (loop = 0x000; loop < 0x400; loop += 8) + __set_intr_stub(loop, __common_exception); + + __set_intr_stub(EXCEP_ITLBMISS, itlb_miss); + __set_intr_stub(EXCEP_DTLBMISS, dtlb_miss); + __set_intr_stub(EXCEP_IAERROR, itlb_aerror); + __set_intr_stub(EXCEP_DAERROR, dtlb_aerror); + __set_intr_stub(EXCEP_BUSERROR, raw_bus_error); + __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault); + __set_intr_stub(EXCEP_SYSCALL0, system_call); + + __set_intr_stub(EXCEP_NMI, nmi_handler); + __set_intr_stub(EXCEP_WDT, nmi_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL0, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL1, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL2, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL3, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL4, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL5, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL6, irq_handler); + + IVAR0 = EXCEP_IRQ_LEVEL0; + IVAR1 = EXCEP_IRQ_LEVEL1; + IVAR2 = EXCEP_IRQ_LEVEL2; + IVAR3 = EXCEP_IRQ_LEVEL3; + IVAR4 = EXCEP_IRQ_LEVEL4; + IVAR5 = EXCEP_IRQ_LEVEL5; + IVAR6 = EXCEP_IRQ_LEVEL6; + + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); + + /* disable all interrupts and set to priority 6 (lowest) */ + for (loop = 0; loop < NR_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; + + /* clear the timers */ + TM0MD = 0; + TM1MD = 0; + TM2MD = 0; + TM3MD = 0; + TM4MD = 0; + TM5MD = 0; + TM6MD = 0; + TM6MDA = 0; + TM6MDB = 0; + TM7MD = 0; + TM8MD = 0; + TM9MD = 0; + TM10MD = 0; + TM11MD = 0; + + calibrate_clock(); +} diff --git a/arch/mn10300/unit-asb2303/Makefile b/arch/mn10300/unit-asb2303/Makefile new file mode 100644 index 00000000000..38a5bb43b0b --- /dev/null +++ b/arch/mn10300/unit-asb2303/Makefile @@ -0,0 +1,6 @@ +############################################################################### +# +# Makefile for the ASB2303 board +# +############################################################################### +obj-y := unit-init.o smc91111.o flash.o leds.o diff --git a/arch/mn10300/unit-asb2303/flash.c b/arch/mn10300/unit-asb2303/flash.c new file mode 100644 index 00000000000..17fe083fcb6 --- /dev/null +++ b/arch/mn10300/unit-asb2303/flash.c @@ -0,0 +1,100 @@ +/* Handle mapping of the flash on the ASB2303 board + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> + +#define ASB2303_PROM_ADDR 0xA0000000 /* Boot PROM */ +#define ASB2303_PROM_SIZE (2 * 1024 * 1024) +#define ASB2303_FLASH_ADDR 0xA4000000 /* System Flash */ +#define ASB2303_FLASH_SIZE (32 * 1024 * 1024) +#define ASB2303_CONFIG_ADDR 0xA6000000 /* System Config EEPROM */ +#define ASB2303_CONFIG_SIZE (8 * 1024) + +/* + * default MTD partition table for both main flash devices, expected to be + * overridden by RedBoot + */ +static struct mtd_partition asb2303_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM /* force read-only */ + }, { + .name = "Kernel", + .size = 0x00400000, + .offset = 0x00040000, + }, { + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00440000 + } +}; + +/* + * the ASB2303 Boot PROM definition + */ +static struct physmap_flash_data asb2303_bootprom_data = { + .width = 2, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_bootprom_resource = { + .start = ASB2303_PROM_ADDR, + .end = ASB2303_PROM_ADDR + ASB2303_PROM_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_bootprom = { + .name = "physmap-flash", + .id = 0, + .dev.platform_data = &asb2303_bootprom_data, + .num_resources = 1, + .resource = &asb2303_bootprom_resource, +}; + +/* + * the ASB2303 System Flash definition + */ +static struct physmap_flash_data asb2303_sysflash_data = { + .width = 4, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_sysflash_resource = { + .start = ASB2303_FLASH_ADDR, + .end = ASB2303_FLASH_ADDR + ASB2303_FLASH_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_sysflash = { + .name = "physmap-flash", + .id = 1, + .dev.platform_data = &asb2303_sysflash_data, + .num_resources = 1, + .resource = &asb2303_sysflash_resource, +}; + +/* + * register the ASB2303 flashes + */ +static int __init asb2303_mtd_init(void) +{ + platform_device_register(&asb2303_bootprom); + platform_device_register(&asb2303_sysflash); + return 0; +} + +module_init(asb2303_mtd_init); diff --git a/arch/mn10300/unit-asb2303/leds.c b/arch/mn10300/unit-asb2303/leds.c new file mode 100644 index 00000000000..cd4bc78ccfc --- /dev/null +++ b/arch/mn10300/unit-asb2303/leds.c @@ -0,0 +1,52 @@ +/* ASB2303 peripheral 7-segment LEDs x1 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <asm/unit/leds.h> + +#if 0 +static const u8 asb2303_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; +#endif + +static const u8 asb2303_led_chase_tbl[6] = { + ~0x02, /* top - segA */ + ~0x04, /* right top - segB */ + ~0x08, /* right bottom - segC */ + ~0x10, /* bottom - segD */ + ~0x20, /* left bottom - segE */ + ~0x40, /* left top - segF */ +}; + +static unsigned asb2303_led_chase; + +void peripheral_leds_display_exception(enum exception_code code) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = 0x6d; /* triple horizontal bar */ +} + +void peripheral_leds_led_chase(void) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = asb2303_led_chase_tbl[asb2303_led_chase]; + asb2303_led_chase++; + if (asb2303_led_chase >= 6) + asb2303_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2303/smc91111.c b/arch/mn10300/unit-asb2303/smc91111.c new file mode 100644 index 00000000000..30875dd6563 --- /dev/null +++ b/arch/mn10300/unit-asb2303/smc91111.c @@ -0,0 +1,52 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/timex.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/unit/smc91111.h> + +static struct resource smc91c111_resources[] = { + [0] = { + .start = SMC91111_BASE, + .end = SMC91111_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMC91111_IRQ, + .end = SMC91111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91c111_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91c111_resources), + .resource = smc91c111_resources, +}; + +/* + * add platform devices + */ +static int __init unit_device_init(void) +{ + platform_device_register(&smc91c111_device); + return 0; +} + +device_initcall(unit_device_init); diff --git a/arch/mn10300/unit-asb2303/unit-init.c b/arch/mn10300/unit-asb2303/unit-init.c new file mode 100644 index 00000000000..14b2c817cff --- /dev/null +++ b/arch/mn10300/unit-asb2303/unit-init.c @@ -0,0 +1,60 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/intctl-regs.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ + /* set up the external interrupts */ + SET_XIRQ_TRIGGER(0, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(2, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(3, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(4, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(5, XIRQ_TRIGGER_LOWLEVEL); +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq); + break; + default: + break; + } + } +} diff --git a/arch/mn10300/unit-asb2305/Makefile b/arch/mn10300/unit-asb2305/Makefile new file mode 100644 index 00000000000..0551022225b --- /dev/null +++ b/arch/mn10300/unit-asb2305/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for the ASB2305 board +# +############################################################################### +obj-y := unit-init.o leds.o + +obj-$(CONFIG_PCI) += pci.o pci-asb2305.o pci-irq.o pci-iomap.o diff --git a/arch/mn10300/unit-asb2305/leds.c b/arch/mn10300/unit-asb2305/leds.c new file mode 100644 index 00000000000..e99dcc9cee1 --- /dev/null +++ b/arch/mn10300/unit-asb2305/leds.c @@ -0,0 +1,124 @@ +/* ASB2305 Peripheral 7-segment LEDs x4 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/unit/leds.h> + +static const u8 asb2305_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; + +static const u32 asb2305_led_chase_tbl[6] = { + ~0x02020202, /* top - segA */ + ~0x04040404, /* right top - segB */ + ~0x08080808, /* right bottom - segC */ + ~0x10101010, /* bottom - segD */ + ~0x20202020, /* left bottom - segE */ + ~0x40404040, /* left top - segF */ +}; + +static unsigned asb2305_led_chase; + +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds_display_exception(enum exception_code code) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(code/0x100) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(code/0x10) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[code % 0x10]; + leds |= 0x6d010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_minssecs(unsigned int time, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(time/600) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/60) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/10) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[time % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_rtc(void) +{ + unsigned int clock; + u8 mins, secs; + + mins = RTMCR; + secs = RTSCR; + + clock = ((mins & 0xf0) >> 4); + clock *= 10; + clock += (mins & 0x0f); + clock *= 6; + + clock += ((secs & 0xf0) >> 4); + clock *= 10; + clock += (secs & 0x0f); + + peripheral_leds7x4_display_minssecs(clock, 0); +} + +void peripheral_leds_led_chase(void) +{ + ASB2305_7SEGLEDS = asb2305_led_chase_tbl[asb2305_led_chase]; + asb2305_led_chase++; + if (asb2305_led_chase >= 6) + asb2305_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c new file mode 100644 index 00000000000..d100ca78846 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -0,0 +1,303 @@ +/* ASB2305 PCI resource stuff + * + * Copyright (C) 2001 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/pci-i386.c + * - Copyright 1997--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include "pci-asb2305.h" + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) +{ +#if 0 + struct pci_dev *dev = data; + + printk(KERN_DEBUG + "### PCIBIOS_ALIGN_RESOURCE(%s,,{%08lx-%08lx,%08lx},%lx)\n", + pci_name(dev), + res->start, + res->end, + res->flags, + size + ); +#endif + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + dev = bus->self; + if (dev) { + for (idx = PCI_BRIDGE_RESOURCES; + idx < PCI_NUM_RESOURCES; + idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + pr = pci_find_parent_resource(dev, r); + if (!r->start || + !pr || + request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of bridge %s\n", + idx, pci_name(dev)); + /* Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. */ + r->flags = 0; + } + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + for_each_pci_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI[%s]: Resource %08lx-%08lx" + " (f=%lx, d=%d, p=%d)\n", + pci_name(dev), r->start, r->end, r->flags, + disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of device %s\n", + idx, pci_name(dev)); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", + pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword( + dev, dev->rom_base_reg, ®); + pci_write_config_dword( + dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + struct resource *r, *pr; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) { + /* Try to use BIOS settings for ROMs, otherwise let + pci_assign_unassigned_resources() allocate the new + addresses. */ + for_each_pci_dev(dev) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + r->end -= r->start; + r->start = 0; + } + } + } + + pci_assign_unassigned_resources(); + + return 0; +} + +fs_initcall(pcibios_assign_resources); + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); +} + +int pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + + for (idx = 0; idx < 6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1 << idx))) + continue; + + r = &dev->resource[idx]; + + if (!r->start && r->end) { + printk(KERN_ERR + "PCI: Device %s not available because of" + " resource collisions\n", + pci_name(dev)); + return -EINVAL; + } + + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + + if (cmd != old_cmd) + pci_write_config_word(dev, PCI_COMMAND, cmd); + + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= VM_LOCKED | VM_IO; + + prot = pgprot_val(vma->vm_page_prot); + prot &= ~_PAGE_CACHE; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored */ + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h new file mode 100644 index 00000000000..84634fa3bce --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -0,0 +1,82 @@ +/* ASB2305 Arch-specific PCI declarations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-i386.h: (c) 1999 Martin Mares <mj@ucw.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _PCI_ASB2305_H +#define _PCI_ASB2305_H + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_SORT 0x100 +#define PCI_BIOS_SORT 0x200 +#define PCI_NO_CHECKS 0x400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 + +extern unsigned int pci_probe; + +/* pci-asb2305.c */ + +extern unsigned int pcibios_max_latency; + +extern void pcibios_resource_survey(void); +extern int pcibios_enable_resources(struct pci_dev *dev, int mask); + +/* pci.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +extern struct irq_routing_table *pcibios_get_irq_routing_table(void); +extern int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, + * 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +extern void pcibios_irq_init(void); +extern void pcibios_fixup_irqs(void); +extern void pcibios_enable_irq(struct pci_dev *dev); + +#endif /* PCI_ASB2305_H */ diff --git a/arch/mn10300/unit-asb2305/pci-iomap.c b/arch/mn10300/unit-asb2305/pci-iomap.c new file mode 100644 index 00000000000..dbceae4307d --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-iomap.c @@ -0,0 +1,31 @@ +/* ASB2305 PCI I/O mapping handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/pci.h> +#include <linux/module.h> + +/* + * Create a virtual mapping cookie for a PCI BAR (memory or IO) + */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + + if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) + return (void __iomem *) start; + + return NULL; +} +EXPORT_SYMBOL(pci_iomap); diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c new file mode 100644 index 00000000000..58cfb44f0ac --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-irq.c @@ -0,0 +1,51 @@ +/* PCI IRQ routing on the MN103E010 based ASB2305 + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * This is simple: All PCI interrupts route through the CPU's XIRQ1 pin [IRQ 35] + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include "pci-asb2305.h" + +void __init pcibios_irq_init(void) +{ +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev = NULL; + u8 line, pin; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + dev->irq = XIRQ1; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, + dev->irq); + } + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line); + } +} + +void __init pcibios_penalize_isa_irq(int irq) +{ +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c new file mode 100644 index 00000000000..1a86425fec4 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci.c @@ -0,0 +1,545 @@ +/* ASB2305 PCI support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from arch/i386/kernel/pci-pc.c + * (c) 1999--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <asm/io.h> +#include "pci-asb2305.h" + +unsigned int pci_probe = 1; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * Functions for accessing PCI configuration space + */ + +#define CONFIG_CMD(bus, devfn, where) \ + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +#define MEM_PAGING_REG (*(volatile __u32 *) 0xBFFFFFF4) +#define CONFIG_ADDRESS (*(volatile __u32 *) 0xBFFFFFF8) +#define CONFIG_DATAL(X) (*(volatile __u32 *) 0xBFFFFFFC) +#define CONFIG_DATAW(X) (*(volatile __u16 *) (0xBFFFFFFC + ((X) & 2))) +#define CONFIG_DATAB(X) (*(volatile __u8 *) (0xBFFFFFFC + ((X) & 3))) + +#define BRIDGEREGB(X) (*(volatile __u8 *) (0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *) (0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *) (0xBE040000 + (X))) + +static inline int __query(const struct pci_bus *bus, unsigned int devfn) +{ +#if 0 + return bus->number == 0 && (devfn == PCI_DEVFN(0, 0)); + return bus->number == 1; + return bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(3, 0)); +#endif + return 1; +} + +/* + * translate Linuxcentric addresses to PCI bus addresses + */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + if (res->flags & IORESOURCE_IO) { + region->start = (res->start & 0x00ffffff); + region->end = (res->end & 0x00ffffff); + } + + if (res->flags & IORESOURCE_MEM) { + region->start = (res->start & 0x03ffffff) | MEM_PAGING_REG; + region->end = (res->end & 0x03ffffff) | MEM_PAGING_REG; + } + +#if 0 + printk(KERN_DEBUG "RES->BUS: %lx-%lx => %lx-%lx\n", + res->start, res->end, region->start, region->end); +#endif +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* + * translate PCI bus addresses to Linuxcentric addresses + */ +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + if (res->flags & IORESOURCE_IO) { + res->start = (region->start & 0x00ffffff) | 0xbe000000; + res->end = (region->end & 0x00ffffff) | 0xbe000000; + } + + if (res->flags & IORESOURCE_MEM) { + res->start = (region->start & 0x03ffffff) | 0xb8000000; + res->end = (region->end & 0x03ffffff) | 0xb8000000; + } + +#if 0 + printk(KERN_INFO "BUS->RES: %lx-%lx => %lx-%lx\n", + region->start, region->end, res->start, res->end); +#endif +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* + * + */ +static int pci_ampci_read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGB(where); + __pcbdebug("=> %02hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAB(where); + if (__query(bus, devfn)) + __pcidebug("=> %02hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGW(where); + __pcbdebug("=> %04hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAW(where); + if (__query(bus, devfn)) + __pcidebug("=> %04hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGL(where); + __pcbdebug("=> %08x", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAL(where); + if (__query(bus, devfn)) + __pcidebug("=> %08x", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %02x", &BRIDGEREGB(where), value); + BRIDGEREGB(where) = value; + } else { + if (bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) && devfn == PCI_DEVFN(3, 0)) + ) + __pcidebug("<= %02x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAB(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %04hx", &BRIDGEREGW(where), value); + BRIDGEREGW(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %04hx", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAW(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %08x", &BRIDGEREGL(where), value); + BRIDGEREGL(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %08x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAL(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + switch (size) { + case 1: + return pci_ampci_read_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_read_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_read_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static int pci_ampci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return pci_ampci_write_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_write_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_write_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static struct pci_ops pci_direct_ampci = { + pci_ampci_read_config, + pci_ampci_write_config, +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __init pci_sanity_check(struct pci_ops *o) +{ + struct pci_bus bus; /* Fake bus and device */ + u32 x; + + bus.number = 0; + + if ((!o->read(&bus, 0, PCI_CLASS_DEVICE, 2, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read(&bus, 0, PCI_VENDOR_ID, 2, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + + printk(KERN_ERROR "PCI: Sanity check failed\n"); + return 0; +} + +static int __init pci_check_direct(void) +{ + unsigned long flags; + + local_irq_save(flags); + + /* + * Check if access works. + */ + if (pci_sanity_check(&pci_direct_ampci)) { + local_irq_restore(flags); + printk(KERN_INFO "PCI: Using configuration ampci\n"); + request_mem_region(0xBE040000, 256, "AMPCI bridge"); + request_mem_region(0xBFFFFFF4, 12, "PCI ampci"); + return 0; + } + + local_irq_restore(flags); + return -ENODEV; +} + +static int __devinit is_valid_resource(struct pci_dev *dev, int idx) +{ + unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; + struct resource *devr = &dev->resource[idx]; + + if (dev->bus) { + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + struct resource *busr = dev->bus->resource[i]; + + if (!busr || (busr->flags ^ devr->flags) & type_mask) + continue; + + if (devr->start && + devr->start >= busr->start && + devr->end <= busr->end) + return 1; + } + } + + return 0; +} + +static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) +{ + struct pci_bus_region region; + int i; + int limit; + + if (dev->bus->number != 0) + return; + + limit = (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) ? + PCI_BRIDGE_RESOURCES : PCI_NUM_RESOURCES; + + for (i = 0; i < limit; i++) { + if (!dev->resource[i].flags) + continue; + + region.start = dev->resource[i].start; + region.end = dev->resource[i].end; + pcibios_bus_to_resource(dev, &dev->resource[i], ®ion); + if (is_valid_resource(dev, i)) + pci_claim_resource(dev, i); + } +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; + + if (bus->self) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(bus->self); + } + + list_for_each_entry(dev, &bus->devices, bus_list) + pcibios_fixup_device_resources(dev); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ +static int __init pcibios_init(void) +{ + ioport_resource.start = 0xA0000000; + ioport_resource.end = 0xDFFFFFFF; + iomem_resource.start = 0xA0000000; + iomem_resource.end = 0xDFFFFFFF; + + if (!pci_probe) + return 0; + + if (pci_check_direct() < 0) { + printk(KERN_WARNING "PCI: No PCI bus detected\n"); + return 0; + } + + printk(KERN_INFO "PCI: Probing PCI hardware [mempage %08x]\n", + MEM_PAGING_REG); + + { +#if 0 + static struct pci_bus am33_root_bus = { + .children = LIST_HEAD_INIT(am33_root_bus.children), + .devices = LIST_HEAD_INIT(am33_root_bus.devices), + .number = 0, + .secondary = 0, + .resource = { &ioport_resource, &iomem_resource }, + }; + + am33_root_bus.ops = pci_root_ops; + list_add_tail(&am33_root_bus.node, &pci_root_buses); + + am33_root_bus.subordinate = pci_do_scan_bus(0); + + pci_root_bus = &am33_root_bus; +#else + pci_root_bus = pci_scan_bus(0, &pci_direct_ampci, NULL); +#endif + } + + pcibios_irq_init(); + pcibios_fixup_irqs(); +#if 0 + pcibios_resource_survey(); +#endif + return 0; +} + +arch_initcall(pcibios_init); + +char *__init pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + + return str; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + err = pcibios_enable_resources(dev, mask); + if (err == 0) + pcibios_enable_irq(dev); + return err; +} + +/* + * disable the ethernet chipset + */ +static void __init unit_disable_pcnet(struct pci_bus *bus, struct pci_ops *o) +{ + u32 x; + + bus->number = 0; + + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, x); + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + o->write(bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, 0x00030001); + o->read (bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, &x); + +#define RDP (*(volatile u32 *) 0xBE030010) +#define RAP (*(volatile u32 *) 0xBE030014) +#define __set_RAP(X) do { RAP = (X); x = RAP; } while (0) +#define __set_RDP(X) do { RDP = (X); x = RDP; } while (0) +#define __get_RDP() ({ RDP & 0xffff; }) + + __set_RAP(0); + __set_RDP(0x0004); /* CSR0 = STOP */ + + __set_RAP(88); /* check CSR88 indicates an Am79C973 */ + BUG_ON(__get_RDP() != 0x5003); + + for (x = 0; x < 100; x++) + asm volatile("nop"); + + __set_RDP(0x0004); /* CSR0 = STOP */ +} + +/* + * initialise the unit hardware + */ +asmlinkage void __init unit_pci_init(void) +{ + struct pci_bus bus; /* Fake bus and device */ + struct pci_ops *o = &pci_direct_ampci; + u32 x; + + set_intr_level(XIRQ1, GxICR_LEVEL_3); + + memset(&bus, 0, sizeof(bus)); + + MEM_PAGING_REG = 0xE8000000; + + /* we need to set up the bridge _now_ or we won't be able to access the + * PCI config registers + */ + BRIDGEREGW(PCI_COMMAND) |= + PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER; + BRIDGEREGW(PCI_STATUS) = 0xF800; + BRIDGEREGB(PCI_LATENCY_TIMER) = 0x10; + BRIDGEREGL(PCI_BASE_ADDRESS_0) = 0x80000000; + BRIDGEREGB(PCI_INTERRUPT_LINE) = 1; + BRIDGEREGL(0x48) = 0x98000000; /* AMPCI base addr */ + BRIDGEREGB(0x41) = 0x00; /* secondary bus + * number */ + BRIDGEREGB(0x42) = 0x01; /* subordinate bus + * number */ + BRIDGEREGB(0x44) = 0x01; + BRIDGEREGL(0x50) = 0x00000001; + BRIDGEREGL(0x58) = 0x00001002; + BRIDGEREGL(0x5C) = 0x00000011; + + /* we also need to set up the PCI-PCI bridge */ + bus.number = 0; + + /* IO: 0x00000000-0x00020000 */ + o->read (&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, x); + + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, 0x01); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, 0x00020000); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, 0xEBB0EA00); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, 0xE9F0E800); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + unit_disable_pcnet(&bus, o); +} diff --git a/arch/mn10300/unit-asb2305/unit-init.c b/arch/mn10300/unit-asb2305/unit-init.c new file mode 100644 index 00000000000..6a352414a35 --- /dev/null +++ b/arch/mn10300/unit-asb2305/unit-init.c @@ -0,0 +1,61 @@ +/* ASB2305 Initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/cpu/serial-regs.h> +#include <asm/unit/serial.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + /* set the 16550 interrupt line to level 3 if not being used for GDB */ + set_intr_level(XIRQ0, GxICR_LEVEL_3); +#endif +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +#ifdef CONFIG_PCI + unit_pci_init(); +#endif +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq); + break; + default: + break; + } + } +} diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 028d8a0fdbf..d929ac84f25 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -75,6 +75,9 @@ config IRQ_PER_CPU bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index c44b8c51f5d..39e7c5a5946 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -190,7 +190,7 @@ c_stop (struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 8dcac0b22d6..26b963c33c8 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -272,6 +272,12 @@ config HOTPLUG_CPU config ARCH_ENABLE_MEMORY_HOTPLUG def_bool y +config ARCH_HAS_WALK_MEMORY + def_bool y + +config ARCH_ENABLE_MEMORY_HOTREMOVE + def_bool y + config KEXEC bool "kexec system call (EXPERIMENTAL)" depends on (PPC_PRPMC2800 || PPC_MULTIPLATFORM) && EXPERIMENTAL diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 93a5c53e342..be5c506779a 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -129,6 +129,39 @@ int __devinit arch_add_memory(int nid, u64 start, u64 size) return __add_pages(zone, start_pfn, nr_pages); } +#ifdef CONFIG_MEMORY_HOTREMOVE +int remove_memory(u64 start, u64 size) +{ + unsigned long start_pfn, end_pfn; + int ret; + + start_pfn = start >> PAGE_SHIFT; + end_pfn = start_pfn + (size >> PAGE_SHIFT); + ret = offline_pages(start_pfn, end_pfn, 120 * HZ); + if (ret) + goto out; + /* Arch-specific calls go here - next patch */ +out: + return ret; +} +#endif /* CONFIG_MEMORY_HOTREMOVE */ + +/* + * walk_memory_resource() needs to make sure there is no holes in a given + * memory range. On PPC64, since this range comes from /sysfs, the range + * is guaranteed to be valid, non-overlapping and can not contain any + * holes. By the time we get here (memory add or remove), /proc/device-tree + * is updated and correct. Only reason we need to check against device-tree + * would be if we allow user-land to specify a memory range through a + * system call/ioctl etc. instead of doing offline/online through /sysfs. + */ +int +walk_memory_resource(unsigned long start_pfn, unsigned long nr_pages, void *arg, + int (*func)(unsigned long, unsigned long, void *)) +{ + return (*func)(start_pfn, nr_pages, arg); +} + #endif /* CONFIG_MEMORY_HOTPLUG */ void show_mem(void) diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index f80f90c4d58..ac3390f8190 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -107,19 +107,20 @@ __init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *ptepage; #ifdef CONFIG_HIGHPTE - gfp_t flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT; + gfp_t flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT | __GFP_ZERO; #else - gfp_t flags = GFP_KERNEL | __GFP_REPEAT; + gfp_t flags = GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO; #endif ptepage = alloc_pages(flags, 0); - if (ptepage) - clear_highpage(ptepage); + if (!ptepage) + return NULL; + pgtable_page_ctor(ptepage); return ptepage; } @@ -131,11 +132,12 @@ void pte_free_kernel(struct mm_struct *mm, pte_t *pte) free_page((unsigned long)pte); } -void pte_free(struct mm_struct *mm, struct page *ptepage) +void pte_free(struct mm_struct *mm, pgtable_t ptepage) { #ifdef CONFIG_SMP hash_page_sync(); #endif + pgtable_page_dtor(ptepage); __free_page(ptepage); } diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index df330666ccc..edab631a8dc 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/notifier.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <asm/prom.h> @@ -789,18 +790,16 @@ static int __init cell_iommu_init_disabled(void) static u64 cell_iommu_get_fixed_address(struct device *dev) { u64 cpu_addr, size, best_size, pci_addr = OF_BAD_ADDR; - struct device_node *tmp, *np; + struct device_node *np; const u32 *ranges = NULL; int i, len, best; - np = dev->archdata.of_node; - of_node_get(np); - ranges = of_get_property(np, "dma-ranges", &len); - while (!ranges && np) { - tmp = of_get_parent(np); - of_node_put(np); - np = tmp; + np = of_node_get(dev->archdata.of_node); + while (np) { ranges = of_get_property(np, "dma-ranges", &len); + if (ranges) + break; + np = of_get_next_parent(np); } if (!ranges) { @@ -842,19 +841,18 @@ static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask) if (!dev->dma_mask || !dma_supported(dev, dma_mask)) return -EIO; - if (dma_mask == DMA_BIT_MASK(64)) { - if (cell_iommu_get_fixed_address(dev) == OF_BAD_ADDR) - dev_dbg(dev, "iommu: 64-bit OK, but bad addr\n"); - else { - dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); - set_dma_ops(dev, &dma_iommu_fixed_ops); - cell_dma_dev_setup(dev); - } + if (dma_mask == DMA_BIT_MASK(64) && + cell_iommu_get_fixed_address(dev) != OF_BAD_ADDR) + { + dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); + set_dma_ops(dev, &dma_iommu_fixed_ops); } else { dev_dbg(dev, "iommu: not 64-bit, using default ops\n"); set_dma_ops(dev, get_pci_dma_ops()); } + cell_dma_dev_setup(dev); + *dev->dma_mask = dma_mask; return 0; @@ -918,6 +916,18 @@ static int __init cell_iommu_fixed_mapping_init(void) return -1; } + /* We must have dma-ranges properties for fixed mapping to work */ + for (np = NULL; (np = of_find_all_nodes(np));) { + if (of_find_property(np, "dma-ranges", NULL)) + break; + } + of_node_put(np); + + if (!np) { + pr_debug("iommu: no dma-ranges found, no fixed mapping\n"); + return -1; + } + /* The default setup is to have the fixed mapping sit after the * dynamic region, so find the top of the largest IOMMU window * on any axon, then add the size of RAM and that's our max value. @@ -981,8 +991,8 @@ static int __init cell_iommu_fixed_mapping_init(void) dsize = htab_size_bytes; } - pr_debug("iommu: setting up %d, dynamic window %lx-%lx " \ - "fixed window %lx-%lx\n", iommu->nid, dbase, + printk(KERN_DEBUG "iommu: node %d, dynamic window 0x%lx-0x%lx " + "fixed window 0x%lx-0x%lx\n", iommu->nid, dbase, dbase + dsize, fbase, fbase + fsize); cell_iommu_setup_page_tables(iommu, dbase, dsize, fbase, fsize); @@ -998,8 +1008,6 @@ static int __init cell_iommu_fixed_mapping_init(void) dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; set_pci_dma_ops(&dma_iommu_ops); - printk(KERN_DEBUG "IOMMU fixed mapping established.\n"); - return 0; } diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index 50d98a154aa..64eb15b2204 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -288,6 +288,12 @@ static void spu_backing_runcntl_write(struct spu_context *ctx, u32 val) spin_lock(&ctx->csa.register_lock); ctx->csa.prob.spu_runcntl_RW = val; if (val & SPU_RUNCNTL_RUNNABLE) { + ctx->csa.prob.spu_status_R &= + ~SPU_STATUS_STOPPED_BY_STOP & + ~SPU_STATUS_STOPPED_BY_HALT & + ~SPU_STATUS_SINGLE_STEP & + ~SPU_STATUS_INVALID_INSTR & + ~SPU_STATUS_INVALID_CH; ctx->csa.prob.spu_status_R |= SPU_STATUS_RUNNING; } else { ctx->csa.prob.spu_status_R &= ~SPU_STATUS_RUNNING; diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index eff4d291ba8..e46d300e21a 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -108,7 +108,7 @@ int spufs_handle_class1(struct spu_context *ctx) u64 ea, dsisr, access; unsigned long flags; unsigned flt = 0; - int ret, ret2; + int ret; /* * dar and dsisr get passed from the registers @@ -148,13 +148,10 @@ int spufs_handle_class1(struct spu_context *ctx) ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); /* - * If spu_acquire fails due to a pending signal we just want to return - * EINTR to userspace even if that means missing the dma restart or - * updating the page fault statistics. + * This is nasty: we need the state_mutex for all the bookkeeping even + * if the syscall was interrupted by a signal. ewww. */ - ret2 = spu_acquire(ctx); - if (ret2) - goto out; + mutex_lock(&ctx->state_mutex); /* * Clear dsisr under ctxt lock after handling the fault, so that @@ -185,7 +182,6 @@ int spufs_handle_class1(struct spu_context *ctx) } else spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); - out: spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 1018acd1746..c66c3756970 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -358,6 +358,7 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, { struct spu_context *ctx = vma->vm_file->private_data; unsigned long area, offset = address - vma->vm_start; + int ret = 0; spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx); @@ -379,7 +380,7 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, if (ctx->state == SPU_STATE_SAVED) { up_read(¤t->mm->mmap_sem); spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx); - spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); + ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu); down_read(¤t->mm->mmap_sem); } else { @@ -388,7 +389,8 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu); } - spu_release(ctx); + if (!ret) + spu_release(ctx); return NOPFN_REFAULT; } @@ -460,7 +462,7 @@ static int spufs_cntl_open(struct inode *inode, struct file *file) if (!i->i_openers++) ctx->cntl = inode->i_mapping; mutex_unlock(&ctx->mapping_lock); - return spufs_attr_open(inode, file, spufs_cntl_get, + return simple_attr_open(inode, file, spufs_cntl_get, spufs_cntl_set, "0x%08lx"); } @@ -470,7 +472,7 @@ spufs_cntl_release(struct inode *inode, struct file *file) struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; - spufs_attr_release(inode, file); + simple_attr_release(inode, file); mutex_lock(&ctx->mapping_lock); if (!--i->i_openers) @@ -482,8 +484,8 @@ spufs_cntl_release(struct inode *inode, struct file *file) static const struct file_operations spufs_cntl_fops = { .open = spufs_cntl_open, .release = spufs_cntl_release, - .read = spufs_attr_read, - .write = spufs_attr_write, + .read = simple_attr_read, + .write = simple_attr_write, .mmap = spufs_cntl_mmap, }; @@ -755,23 +757,25 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf, count = spu_acquire(ctx); if (count) - return count; + goto out; /* wait only for the first element */ count = 0; if (file->f_flags & O_NONBLOCK) { - if (!spu_ibox_read(ctx, &ibox_data)) + if (!spu_ibox_read(ctx, &ibox_data)) { count = -EAGAIN; + goto out_unlock; + } } else { count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); + if (count) + goto out; } - if (count) - goto out; /* if we can't write at all, return -EFAULT */ count = __put_user(ibox_data, udata); if (count) - goto out; + goto out_unlock; for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { int ret; @@ -788,9 +792,9 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf, break; } -out: +out_unlock: spu_release(ctx); - +out: return count; } @@ -905,7 +909,7 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, count = spu_acquire(ctx); if (count) - return count; + goto out; /* * make sure we can at least write one element, by waiting @@ -913,14 +917,16 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, */ count = 0; if (file->f_flags & O_NONBLOCK) { - if (!spu_wbox_write(ctx, wbox_data)) + if (!spu_wbox_write(ctx, wbox_data)) { count = -EAGAIN; + goto out_unlock; + } } else { count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); + if (count) + goto out; } - if (count) - goto out; /* write as much as possible */ for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { @@ -934,8 +940,9 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, break; } -out: +out_unlock: spu_release(ctx); +out: return count; } @@ -1598,12 +1605,11 @@ static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, } else { ret = spufs_wait(ctx->mfc_wq, spufs_read_mfc_tagstatus(ctx, &status)); + if (ret) + goto out; } spu_release(ctx); - if (ret) - goto out; - ret = 4; if (copy_to_user(buffer, &status, 4)) ret = -EFAULT; @@ -1732,6 +1738,8 @@ static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, int status; ret = spufs_wait(ctx->mfc_wq, spu_send_mfc_command(ctx, cmd, &status)); + if (ret) + goto out; if (status) ret = status; } @@ -1785,7 +1793,7 @@ static int spufs_mfc_flush(struct file *file, fl_owner_t id) ret = spu_acquire(ctx); if (ret) - return ret; + goto out; #if 0 /* this currently hangs */ ret = spufs_wait(ctx->mfc_wq, @@ -1794,12 +1802,13 @@ static int spufs_mfc_flush(struct file *file, fl_owner_t id) goto out; ret = spufs_wait(ctx->mfc_wq, ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); -out: + if (ret) + goto out; #else ret = 0; #endif spu_release(ctx); - +out: return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 90784c029f2..e6e6559c55e 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -755,8 +755,11 @@ spufs_fill_super(struct super_block *sb, void *data, int silent) .statfs = simple_statfs, .delete_inode = spufs_delete_inode, .drop_inode = generic_delete_inode, + .show_options = generic_show_options, }; + save_mount_options(sb, data); + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index b4814c740d8..fca22e18069 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -53,7 +53,7 @@ int spu_stopped(struct spu_context *ctx, u32 *stat) stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; - if (*stat & stopped) + if (!(*stat & SPU_STATUS_RUNNING) && (*stat & stopped)) return 1; dsisr = ctx->csa.dsisr; @@ -354,8 +354,15 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) do { ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); - if (unlikely(ret)) + if (unlikely(ret)) { + /* + * This is nasty: we need the state_mutex for all the + * bookkeeping even if the syscall was interrupted by + * a signal. ewww. + */ + mutex_lock(&ctx->state_mutex); break; + } spu = ctx->spu; if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))) { @@ -388,16 +395,14 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_SINGLE_STEP))); - if ((status & SPU_STATUS_STOPPED_BY_STOP) && - (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) && - (ctx->state == SPU_STATE_RUNNABLE)) - ctx->stats.libassist++; - - spu_disable_spu(ctx); ret = spu_run_fini(ctx, npc, &status); spu_yield(ctx); + if ((status & SPU_STATUS_STOPPED_BY_STOP) && + (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) + ctx->stats.libassist++; + if ((ret == 0) || ((ret == -ERESTARTSYS) && ((status & SPU_STATUS_STOPPED_BY_HALT) || diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 795a1b52538..2c2fe3c07d7 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -268,6 +268,9 @@ extern char *isolated_loader; * Same as wait_event_interruptible(), except that here * we need to call spu_release(ctx) before sleeping, and * then spu_acquire(ctx) when awoken. + * + * Returns with state_mutex re-acquired when successfull or + * with -ERESTARTSYS and the state_mutex dropped when interrupted. */ #define spufs_wait(wq, condition) \ @@ -278,11 +281,11 @@ extern char *isolated_loader; prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ + spu_release(ctx); \ if (signal_pending(current)) { \ __ret = -ERESTARTSYS; \ break; \ } \ - spu_release(ctx); \ schedule(); \ __ret = spu_acquire(ctx); \ if (__ret) \ diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index ba931be2175..5169ecc3712 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2565,6 +2565,8 @@ static void __init probe_uninorth(void) /* Locate core99 Uni-N */ uninorth_node = of_find_node_by_name(NULL, "uni-n"); + uninorth_maj = 1; + /* Locate G5 u3 */ if (uninorth_node == NULL) { uninorth_node = of_find_node_by_name(NULL, "u3"); @@ -2575,8 +2577,10 @@ static void __init probe_uninorth(void) uninorth_node = of_find_node_by_name(NULL, "u4"); uninorth_maj = 4; } - if (uninorth_node == NULL) + if (uninorth_node == NULL) { + uninorth_maj = 0; return; + } addrp = of_get_property(uninorth_node, "reg", NULL); if (addrp == NULL) @@ -3029,3 +3033,8 @@ void pmac_resume_agp_for_card(struct pci_dev *dev) pmac_agp_resume(pmac_agp_bridge); } EXPORT_SYMBOL(pmac_resume_agp_for_card); + +int pmac_get_uninorth_variant(void) +{ + return uninorth_maj; +} diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index d51368d72e3..294055902f0 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -275,7 +275,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start =c_start, .next = c_next, .stop = c_stop, diff --git a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c index 409fcaa4994..03a79bff127 100644 --- a/arch/ppc/mm/pgtable.c +++ b/arch/ppc/mm/pgtable.c @@ -95,7 +95,7 @@ __init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *ptepage; @@ -106,8 +106,10 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #endif ptepage = alloc_pages(flags, 0); - if (ptepage) + if (ptepage) { clear_highpage(ptepage); + pgtable_page_ctor(ptepage); + } return ptepage; } @@ -119,11 +121,12 @@ void pte_free_kernel(struct mm_struct *mm, pte_t *pte) free_page((unsigned long)pte); } -void pte_free(struct mm_struct *mm, struct page *ptepage) +void pte_free(struct mm_struct *mm, pgtable_t ptepage) { #ifdef CONFIG_SMP hash_page_sync(); #endif + pgtable_page_dtor(ptepage); __free_page(ptepage); } diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 1a2fdb6991d..a4d29025ddb 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -24,6 +24,7 @@ #include <linux/smp.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/seq_file.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/kdebug.h> @@ -218,41 +219,40 @@ void show_registers(struct pt_regs *regs) } /* This is called from fs/proc/array.c */ -char *task_show_regs(struct task_struct *task, char *buffer) +void task_show_regs(struct seq_file *m, struct task_struct *task) { struct pt_regs *regs; regs = task_pt_regs(task); - buffer += sprintf(buffer, "task: %p, ksp: %p\n", + seq_printf(m, "task: %p, ksp: %p\n", task, (void *)task->thread.ksp); - buffer += sprintf(buffer, "User PSW : %p %p\n", + seq_printf(m, "User PSW : %p %p\n", (void *) regs->psw.mask, (void *)regs->psw.addr); - buffer += sprintf(buffer, "User GPRS: " FOURLONG, + seq_printf(m, "User GPRS: " FOURLONG, regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); - buffer += sprintf(buffer, "User ACRS: %08x %08x %08x %08x\n", + seq_printf(m, "User ACRS: %08x %08x %08x %08x\n", task->thread.acrs[0], task->thread.acrs[1], task->thread.acrs[2], task->thread.acrs[3]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[4], task->thread.acrs[5], task->thread.acrs[6], task->thread.acrs[7]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[8], task->thread.acrs[9], task->thread.acrs[10], task->thread.acrs[11]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[12], task->thread.acrs[13], task->thread.acrs[14], task->thread.acrs[15]); - return buffer; } static DEFINE_SPINLOCK(die_lock); diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index e60e0ae1340..019f518cd5a 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -78,6 +78,7 @@ unsigned long *page_table_alloc(int noexec) clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE); page->index = (addr_t) table; } + pgtable_page_ctor(page); table = (unsigned long *) page_to_phys(page); clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE); return table; @@ -87,6 +88,7 @@ void page_table_free(unsigned long *table) { unsigned long *shadow = get_shadow_pte(table); + pgtable_page_dtor(virt_to_page(table)); if (shadow) free_page((unsigned long) shadow); free_page((unsigned long) table); diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 1c3a90835c7..6e035d1cf78 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -89,6 +89,9 @@ config ARCH_HAS_ILOG2_U64 config ARCH_NO_VIRT_TO_BUS def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" menu "System type" diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index 3008c00eea6..8250e017bd4 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -263,7 +263,7 @@ struct sq_sysfs_attr { ssize_t (*store)(const char *buf, size_t count); }; -#define to_sq_sysfs_attr(attr) container_of(attr, struct sq_sysfs_attr, attr) +#define to_sq_sysfs_attr(a) container_of(a, struct sq_sysfs_attr, attr) static ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 99f8971716d..7c674a3503b 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -27,6 +27,13 @@ config ARCH_NO_VIRT_TO_BUS config OF def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + source "init/Kconfig" menu "General machine setup" diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 77460e316a0..19186ce8850 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -20,7 +20,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/smp.h> #include <linux/reboot.h> #include <linux/delay.h> @@ -567,38 +566,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, } /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - unsigned long first_stack_page; - - dump->magic = SUNOS_CORE_MAGIC; - dump->len = sizeof(struct user); - dump->regs.psr = regs->psr; - dump->regs.pc = regs->pc; - dump->regs.npc = regs->npc; - dump->regs.y = regs->y; - /* fuck me plenty */ - memcpy(&dump->regs.regs[0], ®s->u_regs[1], (sizeof(unsigned long) * 15)); - dump->uexec = current->thread.core_exec; - dump->u_tsize = (((unsigned long) current->mm->end_code) - - ((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1); - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))); - dump->u_dsize -= dump->u_tsize; - dump->u_dsize &= ~(PAGE_SIZE - 1); - first_stack_page = (regs->u_regs[UREG_FP] & ~(PAGE_SIZE - 1)); - dump->u_ssize = (TASK_SIZE - first_stack_page) & ~(PAGE_SIZE - 1); - memcpy(&dump->fpu.fpstatus.fregs.regs[0], ¤t->thread.float_regs[0], (sizeof(unsigned long) * 32)); - dump->fpu.fpstatus.fsr = current->thread.fsr; - dump->fpu.fpstatus.flags = dump->fpu.fpstatus.extra = 0; - dump->fpu.fpstatus.fpq_count = current->thread.fpqdepth; - memcpy(&dump->fpu.fpstatus.fpq[0], ¤t->thread.fpqueue[0], - ((sizeof(unsigned long) * 2) * 16)); - dump->sigcode = 0; -} - -/* * fill in the fpu structure for a core dump. */ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs) diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index d07bc74773a..3cf78f16084 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -16,7 +16,6 @@ #include <linux/initrd.h> #include <asm/smp.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/fs.h> diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index ef647acc479..c1025e55165 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -51,7 +51,6 @@ #ifdef CONFIG_PCI #include <asm/ebus.h> #endif -#include <asm/a.out.h> #include <asm/io-unit.h> #include <asm/bug.h> @@ -214,8 +213,6 @@ EXPORT_SYMBOL(kunmap_atomic); EXPORT_SYMBOL(svr4_setcontext); EXPORT_SYMBOL(svr4_getcontext); -EXPORT_SYMBOL(dump_thread); - /* prom symbols */ EXPORT_SYMBOL(idprom); EXPORT_SYMBOL(prom_root_node); diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c index 36b4d24988f..2d9cd65160a 100644 --- a/arch/sparc/mm/loadmmu.c +++ b/arch/sparc/mm/loadmmu.c @@ -14,7 +14,6 @@ #include <asm/system.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/a.out.h> #include <asm/mmu_context.h> #include <asm/oplib.h> diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 17b485f2825..23d3291a3e8 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -34,7 +34,6 @@ #include <asm/sbus.h> #include <asm/asi.h> #include <asm/msi.h> -#include <asm/a.out.h> #include <asm/mmu_context.h> #include <asm/io-unit.h> #include <asm/cacheflush.h> @@ -490,14 +489,17 @@ srmmu_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return (pte_t *)srmmu_get_nocache(PTE_SIZE, PTE_SIZE); } -static struct page * +static pgtable_t srmmu_pte_alloc_one(struct mm_struct *mm, unsigned long address) { unsigned long pte; + struct page *page; if ((pte = (unsigned long)srmmu_pte_alloc_one_kernel(mm, address)) == 0) return NULL; - return pfn_to_page( __nocache_pa(pte) >> PAGE_SHIFT ); + page = pfn_to_page( __nocache_pa(pte) >> PAGE_SHIFT ); + pgtable_page_ctor(page); + return page; } static void srmmu_free_pte_fast(pte_t *pte) @@ -505,10 +507,11 @@ static void srmmu_free_pte_fast(pte_t *pte) srmmu_free_nocache((unsigned long)pte, PTE_SIZE); } -static void srmmu_pte_free(struct page *pte) +static void srmmu_pte_free(pgtable_t pte) { unsigned long p; + pgtable_page_dtor(pte); p = (unsigned long)page_address(pte); /* Cached address (for test) */ if (p == 0) BUG(); diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 0729305f2f5..c0442e8c4b1 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1947,12 +1947,17 @@ static pte_t *sun4c_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -static struct page *sun4c_pte_alloc_one(struct mm_struct *mm, unsigned long address) +static pgtable_t sun4c_pte_alloc_one(struct mm_struct *mm, unsigned long address) { - pte_t *pte = sun4c_pte_alloc_one_kernel(mm, address); + pte_t *pte; + struct page *page; + + pte = sun4c_pte_alloc_one_kernel(mm, address); if (pte == NULL) return NULL; - return virt_to_page(pte); + page = virt_to_page(pte); + pgtable_page_ctor(page); + return page; } static inline void sun4c_free_pte_fast(pte_t *pte) @@ -1962,8 +1967,9 @@ static inline void sun4c_free_pte_fast(pte_t *pte) pgtable_cache_size++; } -static void sun4c_pte_free(struct page *pte) +static void sun4c_pte_free(pgtable_t pte) { + pgtable_page_dtor(pte); sun4c_free_pte_fast(page_address(pte)); } diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index a8c6366f05a..b810f2b7526 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -81,6 +81,9 @@ config GENERIC_HARDIRQS_NO__DO_IRQ bool def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + choice prompt "Kernel page size" default SPARC64_PAGE_SIZE_8KB @@ -380,17 +383,11 @@ config COMPAT bool depends on SPARC32_COMPAT default y - -config BINFMT_ELF32 - bool "Kernel support for 32-bit ELF binaries" - depends on SPARC32_COMPAT - help - This allows you to run 32-bit Linux/ELF binaries on your Ultra. - Everybody wants this; say Y. + select COMPAT_BINFMT_ELF config BINFMT_AOUT32 bool "Kernel support for 32-bit (ie. SunOS) a.out binaries" - depends on SPARC32_COMPAT + depends on SPARC32_COMPAT && ARCH_SUPPORTS_AOUT help This allows you to run 32-bit a.out format binaries on your Ultra. If you want to run SunOS binaries (see SunOS binary emulation below) diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 4b78b24ef41..1bf5b187de4 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o \ obj-$(CONFIG_PCI_MSI) += pci_msi.o obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o -obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index 92c1b36a2e1..9877f2d7672 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -32,6 +32,7 @@ #include <asm/uaccess.h> #include <asm/pgalloc.h> #include <asm/mmu_context.h> +#include <asm/a.out-core.h> static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs); static int load_aout32_library(struct file*); @@ -101,7 +102,7 @@ static int aout32_core_dump(long signr, struct pt_regs *regs, struct file *file, current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm)); dump.signal = signr; - dump_thread(regs, &dump); + aout_dump_thread(regs, &dump); /* If the size of the dump file exceeds the rlimit, then see what would happen if we wrote the stack, but not the data area. */ diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c deleted file mode 100644 index d141300e76b..00000000000 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. - * - * Copyright (C) 1995, 1996, 1997, 1998, 2008 David S. Miller (davem@davemloft.net) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - */ - -#define ELF_ARCH EM_SPARC -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB; - -/* Format is: - * G0 --> G7 - * O0 --> O7 - * L0 --> L7 - * I0 --> I7 - * PSR, PC, nPC, Y, WIM, TBR - */ -typedef unsigned int elf_greg_t; -#define ELF_NGREG 38 -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -typedef struct { - union { - unsigned int pr_regs[32]; - unsigned long pr_dregs[16]; - } pr_fr; - unsigned int __unused; - unsigned int pr_fsr; - unsigned char pr_qcnt; - unsigned char pr_q_entrysize; - unsigned char pr_en; - unsigned int pr_q[64]; -} elf_fpregset_t; - -/* UltraSparc extensions. Still unused, but will be eventually. */ -typedef struct { - unsigned int pr_type; - unsigned int pr_align; - union { - struct { - union { - unsigned int pr_regs[32]; - unsigned long pr_dregs[16]; - long double pr_qregs[8]; - } pr_xfr; - } pr_v8p; - unsigned int pr_xfsr; - unsigned int pr_fprs; - unsigned int pr_xg[8]; - unsigned int pr_xo[8]; - unsigned long pr_tstate; - unsigned int pr_filler[8]; - } pr_un; -} elf_xregset_t; - -#define elf_check_arch(x) (((x)->e_machine == EM_SPARC) || ((x)->e_machine == EM_SPARC32PLUS)) - -#define ELF_ET_DYN_BASE 0x70000000 - - -#include <asm/processor.h> -#include <linux/module.h> -#include <linux/elfcore.h> -#include <linux/compat.h> - -#define elf_prstatus elf_prstatus32 -struct elf_prstatus32 -{ - struct elf_siginfo pr_info; /* Info associated with signal */ - short pr_cursig; /* Current signal */ - unsigned int pr_sigpend; /* Set of pending signals */ - unsigned int pr_sighold; /* Set of held signals */ - pid_t pr_pid; - pid_t pr_ppid; - pid_t pr_pgrp; - pid_t pr_sid; - struct compat_timeval pr_utime; /* User time */ - struct compat_timeval pr_stime; /* System time */ - struct compat_timeval pr_cutime; /* Cumulative user time */ - struct compat_timeval pr_cstime; /* Cumulative system time */ - elf_gregset_t pr_reg; /* GP registers */ - int pr_fpvalid; /* True if math co-processor being used. */ -}; - -#define elf_prpsinfo elf_prpsinfo32 -struct elf_prpsinfo32 -{ - char pr_state; /* numeric process state */ - char pr_sname; /* char for pr_state */ - char pr_zomb; /* zombie */ - char pr_nice; /* nice val */ - unsigned int pr_flag; /* flags */ - u16 pr_uid; - u16 pr_gid; - pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; - /* Lots missing */ - char pr_fname[16]; /* filename of executable */ - char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ -}; - -#include <linux/highuid.h> - -#undef NEW_TO_OLD_UID -#undef NEW_TO_OLD_GID -#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) -#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) - -#include <linux/time.h> - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static inline void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - unsigned long jiffies = cputime_to_jiffies(cputime); - value->tv_usec = (jiffies % HZ) * (1000000L / HZ); - value->tv_sec = jiffies / HZ; -} - -#undef start_thread -#define start_thread start_thread32 -#define init_elf_binfmt init_elf32_binfmt - -MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit SparcLinux binaries on the Ultra"); -MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek"); - -#undef MODULE_DESCRIPTION -#undef MODULE_AUTHOR - -#include <asm/a.out.h> - -#undef TASK_SIZE -#define TASK_SIZE STACK_TOP32 - -#include "../../../fs/binfmt_elf.c" diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 30431bd24e1..5ec06c8c7fe 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -522,7 +522,7 @@ static struct irq_chip sun4v_virq = { .set_affinity = sun4v_virt_set_affinity, }; -static void fastcall pre_flow_handler(unsigned int virt_irq, +static void pre_flow_handler(unsigned int virt_irq, struct irq_desc *desc) { struct irq_handler_data *data = get_irq_chip_data(virt_irq); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index ca7cdfd55f7..1b237917498 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -24,7 +24,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/reboot.h> #include <linux/delay.h> #include <linux/compat.h> @@ -725,17 +724,6 @@ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) return retval; } -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - /* Only should be used for SunOS and ancient a.out - * SparcLinux binaries... Not worth implementing. - */ - memset(dump, 0, sizeof(struct user)); -} - typedef struct { union { unsigned int pr_regs[32]; diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 51f012410f9..9a1ba1fe859 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -684,72 +684,39 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) return &user_sparc64_view; } -long arch_ptrace(struct task_struct *child, long request, long addr, long data) +struct compat_fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct compat_fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; +}; + +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { - long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; - const struct user_regset_view *view; + const struct user_regset_view *view = task_user_regset_view(child); + compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; + struct pt_regs32 __user *pregs; + struct compat_fps __user *fps; + unsigned long addr2 = caddr2; + unsigned long addr = caddr; + unsigned long data = cdata; int ret; - if (test_thread_flag(TIF_32BIT)) - addr2 &= 0xffffffffUL; + pregs = (struct pt_regs32 __user *) addr; + fps = (struct compat_fps __user *) addr; - view = task_user_regset_view(child); - - switch(request) { + switch (request) { case PTRACE_PEEKUSR: ret = (addr != 0) ? -EIO : 0; break; - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp64; - unsigned int tmp32; - int copied; - - ret = -EIO; - if (test_thread_flag(TIF_32BIT)) { - copied = access_process_vm(child, addr, - &tmp32, sizeof(tmp32), 0); - if (copied == sizeof(tmp32)) - ret = put_user(tmp32, - (unsigned int __user *) data); - } else { - copied = access_process_vm(child, addr, - &tmp64, sizeof(tmp64), 0); - if (copied == sizeof(tmp64)) - ret = put_user(tmp64, - (unsigned long __user *) data); - } - break; - } - - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - unsigned long tmp64; - unsigned int tmp32; - int copied; - - ret = -EIO; - if (test_thread_flag(TIF_32BIT)) { - tmp32 = data; - copied = access_process_vm(child, addr, - &tmp32, sizeof(tmp32), 1); - if (copied == sizeof(tmp32)) - ret = 0; - } else { - tmp64 = data; - copied = access_process_vm(child, addr, - &tmp64, sizeof(tmp64), 1); - if (copied == sizeof(tmp64)) - ret = 0; - } - break; - } - - case PTRACE_GETREGS: { - struct pt_regs32 __user *pregs = - (struct pt_regs32 __user *) addr; - + case PTRACE_GETREGS: ret = copy_regset_to_user(child, view, REGSET_GENERAL, 32 * sizeof(u32), 4 * sizeof(u32), @@ -760,29 +727,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) 15 * sizeof(u32), &pregs->u_regs[0]); break; - } - - case PTRACE_GETREGS64: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } - break; - } - - case PTRACE_SETREGS: { - struct pt_regs32 __user *pregs = - (struct pt_regs32 __user *) addr; + case PTRACE_SETREGS: ret = copy_regset_from_user(child, view, REGSET_GENERAL, 32 * sizeof(u32), 4 * sizeof(u32), @@ -793,39 +739,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) 15 * sizeof(u32), &pregs->u_regs[0]); break; - } - - case PTRACE_SETREGS64: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } - break; - } - - case PTRACE_GETFPREGS: { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; + case PTRACE_GETFPREGS: ret = copy_regset_to_user(child, view, REGSET_FP, 0 * sizeof(u32), 32 * sizeof(u32), @@ -843,36 +758,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = -EFAULT; } break; - } - - case PTRACE_GETFPREGS64: { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - }; - struct fps __user *fps = (struct fps __user *) addr; - - ret = copy_regset_to_user(child, view, REGSET_FP, - 0 * sizeof(u64), - 33 * sizeof(u64), - fps); - break; - } - - case PTRACE_SETFPREGS: { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; + case PTRACE_SETFPREGS: ret = copy_regset_from_user(child, view, REGSET_FP, 0 * sizeof(u32), 32 * sizeof(u32), @@ -883,21 +770,94 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) 1 * sizeof(u32), &fps->fsr); break; + + case PTRACE_READTEXT: + case PTRACE_READDATA: + ret = ptrace_readdata(child, addr, + (char __user *)addr2, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; + + case PTRACE_WRITETEXT: + case PTRACE_WRITEDATA: + ret = ptrace_writedata(child, (char __user *) addr2, + addr, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; + + default: + ret = compat_ptrace_request(child, request, addr, data); + break; } - case PTRACE_SETFPREGS64: { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - }; - struct fps __user *fps = (struct fps __user *) addr; + return ret; +} + +struct fps { + unsigned int regs[64]; + unsigned long fsr; +}; + +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + const struct user_regset_view *view = task_user_regset_view(child); + struct pt_regs __user *pregs = (struct pt_regs __user *) addr; + unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; + struct fps __user *fps = (struct fps __user *) addr; + int ret; + + switch (request) { + case PTRACE_PEEKUSR: + ret = (addr != 0) ? -EIO : 0; + break; + case PTRACE_GETREGS64: + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 1 * sizeof(u64), + 15 * sizeof(u64), + &pregs->u_regs[0]); + if (!ret) { + /* XXX doesn't handle 'y' register correctly XXX */ + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 32 * sizeof(u64), + 4 * sizeof(u64), + &pregs->tstate); + } + break; + + case PTRACE_SETREGS64: + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 1 * sizeof(u64), + 15 * sizeof(u64), + &pregs->u_regs[0]); + if (!ret) { + /* XXX doesn't handle 'y' register correctly XXX */ + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 32 * sizeof(u64), + 4 * sizeof(u64), + &pregs->tstate); + } + break; + + case PTRACE_GETFPREGS64: + ret = copy_regset_to_user(child, view, REGSET_FP, + 0 * sizeof(u64), + 33 * sizeof(u64), + fps); + break; + + case PTRACE_SETFPREGS64: ret = copy_regset_to_user(child, view, REGSET_FP, 0 * sizeof(u64), 33 * sizeof(u64), fps); break; - } case PTRACE_READTEXT: case PTRACE_READDATA: @@ -919,16 +879,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = -EIO; break; - case PTRACE_GETEVENTMSG: { - if (test_thread_flag(TIF_32BIT)) - ret = put_user(child->ptrace_message, - (unsigned int __user *) data); - else - ret = put_user(child->ptrace_message, - (unsigned long __user *) data); - break; - } - default: ret = ptrace_request(child, request, addr, data); break; diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index a813441b358..5964d8653ad 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <asm/smp.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/fs.h> diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 8649635d6d7..68db0893039 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -53,7 +53,6 @@ #include <asm/ebus.h> #include <asm/isa.h> #endif -#include <asm/a.out.h> #include <asm/ns87303.h> #include <asm/timer.h> #include <asm/cpudata.h> diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index c56573a10ee..134d801579f 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -28,7 +28,6 @@ #include <asm/uaccess.h> #include <asm/utrap.h> #include <asm/perfctr.h> -#include <asm/a.out.h> #include <asm/unistd.h> /* #define DEBUG_UNIMP_SYSCALL */ diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 98c468803ce..deaba2bd053 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -57,7 +57,6 @@ #include <asm/fpumacro.h> #include <asm/semaphore.h> #include <asm/mmu_context.h> -#include <asm/a.out.h> #include <asm/compat_signal.h> asmlinkage long sys32_chown16(const char __user * filename, u16 user, u16 group) diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index adc62f490f3..6b9b718e24a 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -23,7 +23,7 @@ sys_call_table32: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_chown16, sys32_mknod /*15*/ .word sys_chmod, sys32_lchown16, sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys32_setuid16, sys32_getuid16 -/*25*/ .word sys32_vmsplice, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause +/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index c86cb3091a8..d3e48e9701b 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -415,7 +415,7 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) switch (cmd) { case 0: /* getpgrp */ - return task_pgrp_nr(current); + return task_pgrp_vnr(current); case 1: /* setpgrp */ { int (*sys_setpgid)(pid_t,pid_t) = @@ -426,7 +426,7 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) ret = sys_setpgid(0, 0); if (ret) return ret; proc_clear_tty(current); - return task_pgrp_nr(current); + return task_pgrp_vnr(current); } case 2: /* getsid */ { diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 99e51d059a0..dba8e05f028 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -203,17 +203,6 @@ config NR_CPUS depends on SMP default "32" -config NEST_LEVEL - int "Nesting level" - default "0" - help - This is set to the number of layers of UMLs that this UML will be run - in. Normally, this is zero, meaning that it will run directly on the - host. Setting it to one will build a UML that can run inside a UML - that is running on the host. Generally, if you intend this UML to run - inside another UML, set CONFIG_NEST_LEVEL to one more than the host - UML. - config HIGHMEM bool "Highmem support (EXPERIMENTAL)" depends on !64BIT && EXPERIMENTAL diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386 index 717f5d3440e..3cd8a04d66d 100644 --- a/arch/um/Kconfig.i386 +++ b/arch/um/Kconfig.i386 @@ -23,43 +23,6 @@ config SEMAPHORE_SLEEPERS bool default y -choice - prompt "Host memory split" - default HOST_VMSPLIT_3G - help - This is needed when the host kernel on which you run has a non-default - (like 2G/2G) memory split, instead of the customary 3G/1G. If you did - not recompile your own kernel but use the default distro's one, you can - safely accept the "Default split" option. - - It can be enabled on recent (>=2.6.16-rc2) vanilla kernels via - CONFIG_VM_SPLIT_*, or on previous kernels with special patches (-ck - patchset by Con Kolivas, or other ones) - option names match closely the - host CONFIG_VM_SPLIT_* ones. - - A lower setting (where 1G/3G is lowest and 3G/1G is higher) will - tolerate even more "normal" host kernels, but an higher setting will be - stricter. - - So, if you do not know what to do here, say 'Default split'. - -config HOST_VMSPLIT_3G - bool "Default split (3G/1G user/kernel host split)" -config HOST_VMSPLIT_3G_OPT - bool "3G/1G user/kernel host split (for full 1G low memory)" -config HOST_VMSPLIT_2G - bool "2G/2G user/kernel host split" -config HOST_VMSPLIT_1G - bool "1G/3G user/kernel host split" -endchoice - -config TOP_ADDR - hex - default 0xB0000000 if HOST_VMSPLIT_3G_OPT - default 0x78000000 if HOST_VMSPLIT_2G - default 0x40000000 if HOST_VMSPLIT_1G - default 0xC0000000 - config 3_LEVEL_PGTABLES bool "Three-level pagetables (EXPERIMENTAL)" default n @@ -84,3 +47,5 @@ config GENERIC_HWEIGHT bool default y +config ARCH_SUPPORTS_AOUT + def_bool y diff --git a/arch/um/Kconfig.x86_64 b/arch/um/Kconfig.x86_64 index d632e9a89cc..6533b349f06 100644 --- a/arch/um/Kconfig.x86_64 +++ b/arch/um/Kconfig.x86_64 @@ -15,10 +15,6 @@ config SEMAPHORE_SLEEPERS bool default y -config TOP_ADDR - hex - default 0x7fc0000000 - config 3_LEVEL_PGTABLES bool default y @@ -39,3 +35,5 @@ config GENERIC_HWEIGHT bool default y +config ARCH_SUPPORTS_AOUT + def_bool y diff --git a/arch/um/Makefile b/arch/um/Makefile index cb4af9bf207..dbeab15e7bb 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -79,13 +79,6 @@ KERNEL_DEFINES = $(strip -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask \ KBUILD_CFLAGS += $(KERNEL_DEFINES) KBUILD_CFLAGS += $(call cc-option,-fno-unit-at-a-time,) -# These are needed for clean and mrproper, since in that case .config is not -# included; the values here are meaningless - -CONFIG_NEST_LEVEL ?= 0 - -SIZE = ($(CONFIG_NEST_LEVEL) * 0x20000000) - PHONY += linux all: linux @@ -120,10 +113,6 @@ CFLAGS_NO_HARDENING := $(call cc-option, -fno-PIC,) $(call cc-option, -fno-pic,) CONFIG_KERNEL_STACK_ORDER ?= 2 STACK_SIZE := $(shell echo $$[ 4096 * (1 << $(CONFIG_KERNEL_STACK_ORDER)) ] ) -ifndef START - START = $(shell echo $$[ $(TOP_ADDR) - $(SIZE) ] ) -endif - CPPFLAGS_vmlinux.lds = -U$(SUBARCH) -DSTART=$(START) -DELF_ARCH=$(ELF_ARCH) \ -DELF_FORMAT="$(ELF_FORMAT)" -DKERNEL_STACK_SIZE=$(STACK_SIZE) diff --git a/arch/um/defconfig b/arch/um/defconfig index 86db2862f22..59215bc264e 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -56,8 +56,6 @@ CONFIG_X86_TSC=y CONFIG_UML_X86=y # CONFIG_64BIT is not set CONFIG_SEMAPHORE_SLEEPERS=y -# CONFIG_HOST_2G_2G is not set -CONFIG_TOP_ADDR=0xc0000000 # CONFIG_3_LEVEL_PGTABLES is not set CONFIG_ARCH_HAS_SC_SIGNALS=y CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA=y @@ -81,7 +79,6 @@ CONFIG_HOSTFS=y # CONFIG_HPPFS is not set CONFIG_MCONSOLE=y CONFIG_MAGIC_SYSRQ=y -CONFIG_NEST_LEVEL=0 # CONFIG_HIGHMEM is not set CONFIG_KERNEL_STACK_ORDER=0 diff --git a/arch/um/include/as-layout.h b/arch/um/include/as-layout.h index 606bb5c7fdf..cac542d8ff7 100644 --- a/arch/um/include/as-layout.h +++ b/arch/um/include/as-layout.h @@ -57,6 +57,8 @@ extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; extern unsigned long _unprotected_end; extern unsigned long brk_start; +extern unsigned long host_task_size; + extern int linux_main(int argc, char **argv); extern void (*sig_info[])(int, struct uml_pt_regs *); diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 0b6b6273330..32c799e3a49 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -295,6 +295,9 @@ extern void maybe_sigio_broken(int fd, int read); extern int os_arch_prctl(int pid, int code, unsigned long *addr); /* tty.c */ -int get_pty(void); +extern int get_pty(void); + +/* sys-$ARCH/task_size.c */ +extern unsigned long os_get_task_size(void); #endif diff --git a/arch/um/include/tempfile.h b/arch/um/include/tempfile.h deleted file mode 100644 index d441eac936b..00000000000 --- a/arch/um/include/tempfile.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __TEMPFILE_H__ -#define __TEMPFILE_H__ - -extern int make_tempfile(const char *template, char **tempname, int do_unlink); - -#endif diff --git a/arch/um/include/um_mmu.h b/arch/um/include/um_mmu.h index 82865fcf687..f575ff91f2a 100644 --- a/arch/um/include/um_mmu.h +++ b/arch/um/include/um_mmu.h @@ -13,6 +13,7 @@ typedef struct mm_context { struct mm_id id; struct uml_ldt ldt; + struct page **stub_pages; } mm_context_t; extern void __switch_mm(struct mm_id * mm_idp); diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index 76a62c0cb2b..f5d7f4569ba 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -25,7 +25,7 @@ void flush_thread(void) ret = unmap(¤t->mm->context.id, 0, STUB_START, 0, &data); ret = ret || unmap(¤t->mm->context.id, STUB_END, - TASK_SIZE - STUB_END, 1, &data); + host_task_size - STUB_END, 1, &data); if (ret) { printk(KERN_ERR "flush_thread - clearing address space failed, " "err = %d\n", ret); diff --git a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c index 5311ee93ede..66e2a305a8d 100644 --- a/arch/um/kernel/ksyms.c +++ b/arch/um/kernel/ksyms.c @@ -5,7 +5,6 @@ #include "linux/module.h" #include "linux/syscalls.h" -#include "asm/a.out.h" #include "asm/tlbflush.h" #include "asm/uaccess.h" #include "as-layout.h" @@ -60,7 +59,6 @@ EXPORT_SYMBOL(os_accept_connection); EXPORT_SYMBOL(os_rcv_fd); EXPORT_SYMBOL(run_helper); EXPORT_SYMBOL(start_thread); -EXPORT_SYMBOL(dump_thread); #ifdef CONFIG_SMP diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index d872fdce1d7..2627ce82e91 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -354,11 +354,13 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *pte; pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); + if (pte) + pgtable_page_ctor(pte); return pte; } diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index c07961bedb7..fc50d2f959d 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -258,10 +258,6 @@ void cpu_idle(void) default_idle(); } -void dump_thread(struct pt_regs *regs, struct user *u) -{ -} - int __cant_sleep(void) { return in_atomic() || irqs_disabled() || in_interrupt(); /* Is in_interrupt() really needed? */ diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 78b3e9f69d5..0cd9a7a05e7 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -91,6 +91,8 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm) goto out_free; } + to_mm->stub_pages = NULL; + return 0; out_free: @@ -126,6 +128,7 @@ void arch_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) pages[0] = virt_to_page(&__syscall_stub_start); pages[1] = virt_to_page(mm->context.id.stack); + mm->context.stub_pages = pages; /* dup_mmap already holds mmap_sem */ err = install_special_mapping(mm, STUB_START, STUB_END - STUB_START, @@ -147,6 +150,8 @@ void arch_exit_mmap(struct mm_struct *mm) { pte_t *pte; + if (mm->context.stub_pages != NULL) + kfree(mm->context.stub_pages); pte = virt_to_pte(mm, STUB_CODE); if (pte != NULL) pte_clear(mm, STUB_CODE, pte); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 468aba990db..a6c1dd1cf5a 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -241,6 +241,11 @@ static struct notifier_block panic_exit_notifier = { }; /* Set during early boot */ +unsigned long task_size; +EXPORT_SYMBOL(task_size); + +unsigned long host_task_size; + unsigned long brk_start; unsigned long end_iomem; EXPORT_SYMBOL(end_iomem); @@ -267,6 +272,13 @@ int __init linux_main(int argc, char **argv) if (have_root == 0) add_arg(DEFAULT_COMMAND_LINE); + host_task_size = os_get_task_size(); + /* + * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps + * out + */ + task_size = host_task_size & PGDIR_MASK; + /* OS sanity checks that need to happen before the kernel runs */ os_early_checks(); @@ -303,7 +315,7 @@ int __init linux_main(int argc, char **argv) highmem = 0; iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; - max_physmem = CONFIG_TOP_ADDR - uml_physmem - iomem_size - MIN_VMALLOC; + max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC; /* * Zones have to begin on a 1 << MAX_ORDER page boundary, @@ -335,7 +347,7 @@ int __init linux_main(int argc, char **argv) } virtmem_size = physmem_size; - avail = CONFIG_TOP_ADDR - start_vm; + avail = TASK_SIZE - start_vm; if (physmem_size > avail) virtmem_size = avail; end_vm = start_vm + virtmem_size; diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c index b8d8c9ca8d4..57e3d46c989 100644 --- a/arch/um/os-Linux/aio.c +++ b/arch/um/os-Linux/aio.c @@ -142,7 +142,7 @@ static int do_not_aio(struct aio_thread_req *req) if (actual != req->offset) return -errno; - switch(req->type) { + switch (req->type) { case AIO_READ: n = read(req->io_fd, req->buf, req->len); break; diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c index 04f11b9f1ac..046a131f610 100644 --- a/arch/um/os-Linux/drivers/ethertap_kern.c +++ b/arch/um/os-Linux/drivers/ethertap_kern.c @@ -6,7 +6,7 @@ * Licensed under the GPL. */ -#include "linux/init.h" +#include <linux/init.h> #include <linux/netdevice.h> #include "etap.h" #include "net_kern.h" @@ -30,10 +30,10 @@ static void etap_init(struct net_device *dev, void *data) epri->control_fd = -1; epri->dev = dev; - printk("ethertap backend - %s", epri->dev_name); + printk(KERN_INFO "ethertap backend - %s", epri->dev_name); if (epri->gate_addr != NULL) - printk(", IP = %s", epri->gate_addr); - printk("\n"); + printk(KERN_CONT ", IP = %s", epri->gate_addr); + printk(KERN_CONT "\n"); } static int etap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp) diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c index 9d384807b07..6b9e33d5de2 100644 --- a/arch/um/os-Linux/drivers/tuntap_kern.c +++ b/arch/um/os-Linux/drivers/tuntap_kern.c @@ -29,10 +29,10 @@ static void tuntap_init(struct net_device *dev, void *data) tpri->fd = -1; tpri->dev = dev; - printk("TUN/TAP backend - "); + printk(KERN_INFO "TUN/TAP backend - "); if (tpri->gate_addr != NULL) - printk("IP = %s", tpri->gate_addr); - printk("\n"); + printk(KERN_CONT "IP = %s", tpri->gate_addr); + printk(KERN_CONT "\n"); } static int tuntap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp) diff --git a/arch/um/os-Linux/include/file.h b/arch/um/os-Linux/include/file.h index d82711efacf..fe71be24bd5 100644 --- a/arch/um/os-Linux/include/file.h +++ b/arch/um/os-Linux/include/file.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -9,14 +9,3 @@ #define DEV_NULL "/dev/null" #endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index eedc2d88ef8..93a11d7edfa 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c @@ -1,22 +1,21 @@ +/* + * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL + */ + #include <stdio.h> -#include <stdlib.h> #include <stddef.h> -#include <stdarg.h> +#include <stdlib.h> #include <unistd.h> #include <errno.h> -#include <string.h> #include <fcntl.h> -#include <sys/types.h> +#include <string.h> #include <sys/mman.h> -#include <sys/statfs.h> -#include "user.h" -#include "mem_user.h" +#include <sys/param.h> #include "init.h" -#include "os.h" -#include "tempfile.h" #include "kern_constants.h" - -#include <sys/param.h> +#include "os.h" +#include "user.h" /* Modified by which_tmpdir, which is called during early boot */ static char *default_tmpdir = "/tmp"; @@ -33,18 +32,19 @@ static void __init find_tempdir(void) int i; char *dir = NULL; - if(tempdir != NULL) /* We've already been called */ + if (tempdir != NULL) + /* We've already been called */ return; - for(i = 0; dirs[i]; i++){ + for (i = 0; dirs[i]; i++) { dir = getenv(dirs[i]); - if((dir != NULL) && (*dir != '\0')) + if ((dir != NULL) && (*dir != '\0')) break; } - if((dir == NULL) || (*dir == '\0')) + if ((dir == NULL) || (*dir == '\0')) dir = default_tmpdir; tempdir = malloc(strlen(dir) + 2); - if(tempdir == NULL){ + if (tempdir == NULL) { fprintf(stderr, "Failed to malloc tempdir, " "errno = %d\n", errno); return; @@ -53,7 +53,8 @@ static void __init find_tempdir(void) strcat(tempdir, "/"); } -/* This will return 1, with the first character in buf being the +/* + * This will return 1, with the first character in buf being the * character following the next instance of c in the file. This will * read the file as needed. If there's an error, -errno is returned; * if the end of the file is reached, 0 is returned. @@ -64,11 +65,11 @@ static int next(int fd, char *buf, size_t size, char c) size_t len; char *ptr; - while((ptr = strchr(buf, c)) == NULL){ + while ((ptr = strchr(buf, c)) == NULL) { n = read(fd, buf, size - 1); - if(n == 0) + if (n == 0) return 0; - else if(n < 0) + else if (n < 0) return -errno; buf[n] = '\0'; @@ -78,11 +79,12 @@ static int next(int fd, char *buf, size_t size, char c) len = strlen(ptr); memmove(buf, ptr, len + 1); - /* Refill the buffer so that if there's a partial string that we care + /* + * Refill the buffer so that if there's a partial string that we care * about, it will be completed, and we can recognize it. */ n = read(fd, &buf[len], size - len - 1); - if(n < 0) + if (n < 0) return -errno; buf[len + n] = '\0'; @@ -92,7 +94,8 @@ static int next(int fd, char *buf, size_t size, char c) /* which_tmpdir is called only during early boot */ static int checked_tmpdir = 0; -/* Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner +/* + * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner * way to do this than to parse /proc/mounts. statfs will return the * same filesystem magic number and fs id for both /dev and /dev/shm * when they are both tmpfs, so you can't tell if they are different @@ -107,7 +110,7 @@ static void which_tmpdir(void) int fd, found; char buf[128] = { '\0' }; - if(checked_tmpdir) + if (checked_tmpdir) return; checked_tmpdir = 1; @@ -115,28 +118,28 @@ static void which_tmpdir(void) printf("Checking for tmpfs mount on /dev/shm..."); fd = open("/proc/mounts", O_RDONLY); - if(fd < 0){ + if (fd < 0) { printf("failed to open /proc/mounts, errno = %d\n", errno); return; } - while(1){ + while (1) { found = next(fd, buf, ARRAY_SIZE(buf), ' '); - if(found != 1) + if (found != 1) break; - if(!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) + if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) goto found; found = next(fd, buf, ARRAY_SIZE(buf), '\n'); - if(found != 1) + if (found != 1) break; } err: - if(found == 0) + if (found == 0) printf("nothing mounted on /dev/shm\n"); - else if(found < 0) + else if (found < 0) printf("read returned errno %d\n", -found); out: @@ -146,10 +149,10 @@ out: found: found = next(fd, buf, ARRAY_SIZE(buf), ' '); - if(found != 1) + if (found != 1) goto err; - if(strncmp(buf, "tmpfs", strlen("tmpfs"))){ + if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { printf("not tmpfs\n"); goto out; } @@ -159,43 +162,40 @@ found: goto out; } -/* - * This proc still used in tt-mode - * (file: kernel/tt/ptproxy/proxy.c, proc: start_debugger). - * So it isn't 'static' yet. - */ -int __init make_tempfile(const char *template, char **out_tempname, - int do_unlink) +static int __init make_tempfile(const char *template, char **out_tempname, + int do_unlink) { char *tempname; int fd; which_tmpdir(); tempname = malloc(MAXPATHLEN); - if (!tempname) - goto out; + if (tempname == NULL) + return -1; find_tempdir(); + if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) + return -1; + if (template[0] != '/') strcpy(tempname, tempdir); else tempname[0] = '\0'; strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); fd = mkstemp(tempname); - if(fd < 0){ + if (fd < 0) { fprintf(stderr, "open - cannot create %s: %s\n", tempname, strerror(errno)); goto out; } - if(do_unlink && (unlink(tempname) < 0)){ + if (do_unlink && (unlink(tempname) < 0)) { perror("unlink"); goto out; } - if(out_tempname){ + if (out_tempname) { *out_tempname = tempname; - } else { + } else free(tempname); - } return fd; out: free(tempname); @@ -204,27 +204,23 @@ out: #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" -/* - * This proc is used in start_up.c - * So it isn't 'static'. - */ -int __init create_tmp_file(unsigned long long len) +static int __init create_tmp_file(unsigned long long len) { int fd, err; char zero; fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); - if(fd < 0) { + if (fd < 0) exit(1); - } err = fchmod(fd, 0777); - if(err < 0){ + if (err < 0) { perror("fchmod"); exit(1); } - /* Seek to len - 1 because writing a character there will + /* + * Seek to len - 1 because writing a character there will * increase the file size by one byte, to the desired length. */ if (lseek64(fd, len - 1, SEEK_SET) < 0) { @@ -235,7 +231,7 @@ int __init create_tmp_file(unsigned long long len) zero = 0; err = write(fd, &zero, 1); - if(err != 1){ + if (err != 1) { perror("write"); exit(1); } @@ -250,7 +246,7 @@ int __init create_mem_file(unsigned long long len) fd = create_tmp_file(len); err = os_set_exec_close(fd); - if(err < 0){ + if (err < 0) { errno = -err; perror("exec_close"); } @@ -267,11 +263,11 @@ void __init check_tmpexec(void) PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); printf("Checking PROT_EXEC mmap in %s...",tempdir); fflush(stdout); - if(addr == MAP_FAILED){ + if (addr == MAP_FAILED) { err = errno; perror("failed"); close(fd); - if(err == EPERM) + if (err == EPERM) printf("%s must be not mounted noexec\n",tempdir); exit(1); } diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index abf6beae3df..e0477c3ee89 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -224,7 +224,7 @@ int __init can_drop_memory(void) goto out_unmap; } - printk("OK\n"); + printk(UM_KERN_CONT "OK\n"); ok = 1; out_unmap: diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 0fb0cc8d475..3f1694b134c 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -237,7 +237,7 @@ void unblock_signals(void) * interrupts may have arrived and we need to re-enable them and * recheck signals_pending. */ - while(1) { + while (1) { /* * Save and reset save_pending after enabling signals. This * way, signals_pending won't be changed while we're reading it. diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index d36c89c24a4..b14829469fa 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -341,7 +341,7 @@ void userspace(struct uml_pt_regs *regs) int local_using_sysemu; if (getitimer(ITIMER_VIRTUAL, &timer)) - printk("Failed to get itimer, errno = %d\n", errno); + printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno); nsecs = timer.it_value.tv_sec * UM_NSEC_PER_SEC + timer.it_value.tv_usec * UM_NSEC_PER_USEC; nsecs += os_nsecs(); @@ -388,7 +388,7 @@ void userspace(struct uml_pt_regs *regs) if (WIFSTOPPED(status)) { int sig = WSTOPSIG(status); - switch(sig) { + switch (sig) { case SIGSEGV: if (PTRACE_FULL_FAULTINFO || !ptrace_faultinfo) { @@ -641,7 +641,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf) * after returning to the jumper. */ n = setjmp(initial_jmpbuf); - switch(n) { + switch (n) { case INIT_JMP_NEW_THREAD: (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler; (*switch_buf)[0].JB_SP = (unsigned long) stack + diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile index a841262c594..b4bc6ac4f30 100644 --- a/arch/um/os-Linux/sys-i386/Makefile +++ b/arch/um/os-Linux/sys-i386/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-y = registers.o signal.o tls.o +obj-y = registers.o signal.o task_size.o tls.o USER_OBJS := $(obj-y) diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c index d1997ca76e5..f74d853a0ee 100644 --- a/arch/um/os-Linux/sys-i386/registers.c +++ b/arch/um/os-Linux/sys-i386/registers.c @@ -62,10 +62,10 @@ void arch_init_registers(int pid) int err; err = ptrace(PTRACE_GETFPXREGS, pid, 0, fpx_regs); - if(!err) + if (!err) return; - if(errno != EIO) + if (errno != EIO) panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", errno); diff --git a/arch/um/os-Linux/sys-i386/task_size.c b/arch/um/os-Linux/sys-i386/task_size.c new file mode 100644 index 00000000000..48d211b3d9a --- /dev/null +++ b/arch/um/os-Linux/sys-i386/task_size.c @@ -0,0 +1,120 @@ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/mman.h> +#include "longjmp.h" +#include "kern_constants.h" + +static jmp_buf buf; + +static void segfault(int sig) +{ + longjmp(buf, 1); +} + +static int page_ok(unsigned long page) +{ + unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT); + unsigned long n = ~0UL; + void *mapped = NULL; + int ok = 0; + + /* + * First see if the page is readable. If it is, it may still + * be a VDSO, so we go on to see if it's writable. If not + * then try mapping memory there. If that fails, then we're + * still in the kernel area. As a sanity check, we'll fail if + * the mmap succeeds, but gives us an address different from + * what we wanted. + */ + if (setjmp(buf) == 0) + n = *address; + else { + mapped = mmap(address, UM_KERN_PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mapped == MAP_FAILED) + return 0; + if (mapped != address) + goto out; + } + + /* + * Now, is it writeable? If so, then we're in user address + * space. If not, then try mprotecting it and try the write + * again. + */ + if (setjmp(buf) == 0) { + *address = n; + ok = 1; + goto out; + } else if (mprotect(address, UM_KERN_PAGE_SIZE, + PROT_READ | PROT_WRITE) != 0) + goto out; + + if (setjmp(buf) == 0) { + *address = n; + ok = 1; + } + + out: + if (mapped != NULL) + munmap(mapped, UM_KERN_PAGE_SIZE); + return ok; +} + +unsigned long os_get_task_size(void) +{ + struct sigaction sa, old; + unsigned long bottom = 0; + /* + * A 32-bit UML on a 64-bit host gets confused about the VDSO at + * 0xffffe000. It is mapped, is readable, can be reprotected writeable + * and written. However, exec discovers later that it can't be + * unmapped. So, just set the highest address to be checked to just + * below it. This might waste some address space on 4G/4G 32-bit + * hosts, but shouldn't hurt otherwise. + */ + unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT; + unsigned long test; + + printf("Locating the top of the address space ... "); + fflush(stdout); + + /* + * We're going to be longjmping out of the signal handler, so + * SA_DEFER needs to be set. + */ + sa.sa_handler = segfault; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sigaction(SIGSEGV, &sa, &old); + + if (!page_ok(bottom)) { + fprintf(stderr, "Address 0x%x no good?\n", + bottom << UM_KERN_PAGE_SHIFT); + exit(1); + } + + /* This could happen with a 4G/4G split */ + if (page_ok(top)) + goto out; + + do { + test = bottom + (top - bottom) / 2; + if (page_ok(test)) + bottom = test; + else + top = test; + } while (top - bottom > 1); + +out: + /* Restore the old SIGSEGV handling */ + sigaction(SIGSEGV, &old, NULL); + + top <<= UM_KERN_PAGE_SHIFT; + printf("0x%x\n", top); + fflush(stdout); + + return top; +} diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile index a42a4ef02e1..a44a47f8f57 100644 --- a/arch/um/os-Linux/sys-x86_64/Makefile +++ b/arch/um/os-Linux/sys-x86_64/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-y = registers.o prctl.o signal.o +obj-y = registers.o prctl.o signal.o task_size.o USER_OBJS := $(obj-y) diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c index 9bfa789992d..a375853337a 100644 --- a/arch/um/os-Linux/sys-x86_64/registers.c +++ b/arch/um/os-Linux/sys-x86_64/registers.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Copyright (C) 2006 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -7,31 +7,36 @@ #include <sys/ptrace.h> #define __FRAME_OFFSETS #include <asm/ptrace.h> +#include "kern_constants.h" #include "longjmp.h" #include "user.h" int save_fp_registers(int pid, unsigned long *fp_regs) { - if(ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) return -errno; return 0; } int restore_fp_registers(int pid, unsigned long *fp_regs) { - if(ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) return -errno; return 0; } unsigned long get_thread_reg(int reg, jmp_buf *buf) { - switch(reg){ - case RIP: return buf[0]->__rip; - case RSP: return buf[0]->__rsp; - case RBP: return buf[0]->__rbp; + switch (reg) { + case RIP: + return buf[0]->__rip; + case RSP: + return buf[0]->__rsp; + case RBP: + return buf[0]->__rbp; default: - printk("get_thread_regs - unknown register %d\n", reg); + printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n", + reg); return 0; } } diff --git a/arch/um/os-Linux/sys-x86_64/task_size.c b/arch/um/os-Linux/sys-x86_64/task_size.c new file mode 100644 index 00000000000..fad6f57f8ee --- /dev/null +++ b/arch/um/os-Linux/sys-x86_64/task_size.c @@ -0,0 +1,5 @@ +unsigned long os_get_task_size(unsigned long shift) +{ + /* The old value of CONFIG_TOP_ADDR */ + return 0x7fc0000000; +} diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c index 8d27b6d1df9..087ed74ffca 100644 --- a/arch/um/os-Linux/uaccess.c +++ b/arch/um/os-Linux/uaccess.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -16,7 +16,7 @@ unsigned long __do_user_copy(void *to, const void *from, int n, jmp_buf jbuf; *fault_catcher = &jbuf; - if(UML_SETJMP(&jbuf) == 0){ + if (UML_SETJMP(&jbuf) == 0) { (*op)(to, from, n); ret = 0; *faulted_out = 0; diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c index 4c37b1b1d0b..74f49bb9b12 100644 --- a/arch/um/os-Linux/user_syms.c +++ b/arch/um/os-Linux/user_syms.c @@ -34,8 +34,8 @@ EXPORT_SYMBOL(printf); * good; so the versions of these symbols will always match */ #define EXPORT_SYMBOL_PROTO(sym) \ - int sym(void); \ - EXPORT_SYMBOL(sym); + int sym(void); \ + EXPORT_SYMBOL(sym); extern void readdir64(void) __attribute__((weak)); EXPORT_SYMBOL(readdir64); diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c index a6f31d47699..6ea77979531 100644 --- a/arch/um/os-Linux/util.c +++ b/arch/um/os-Linux/util.c @@ -1,39 +1,24 @@ /* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include <stdio.h> #include <stdlib.h> -#include <unistd.h> -#include <limits.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/utsname.h> -#include <sys/param.h> -#include <sys/time.h> -#include "asm/types.h" -#include <ctype.h> -#include <signal.h> -#include <wait.h> #include <errno.h> -#include <stdarg.h> -#include <sched.h> -#include <termios.h> +#include <signal.h> #include <string.h> -#include "kern_util.h" -#include "user.h" -#include "mem_user.h" -#include "init.h" -#include "ptrace_user.h" -#include "uml-config.h" -#include "os.h" -#include "longjmp.h" +#include <termios.h> +#include <wait.h> +#include <sys/mman.h> +#include <sys/utsname.h> #include "kern_constants.h" +#include "os.h" +#include "user.h" void stack_protections(unsigned long address) { - if(mprotect((void *) address, UM_THREAD_SIZE, + if (mprotect((void *) address, UM_THREAD_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) panic("protecting stack failed, errno = %d", errno); } @@ -44,17 +29,19 @@ int raw(int fd) int err; CATCH_EINTR(err = tcgetattr(fd, &tt)); - if(err < 0) + if (err < 0) return -errno; cfmakeraw(&tt); CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); - if(err < 0) + if (err < 0) return -errno; - /* XXX tcsetattr could have applied only some changes - * (and cfmakeraw() is a set of changes) */ + /* + * XXX tcsetattr could have applied only some changes + * (and cfmakeraw() is a set of changes) + */ return 0; } diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig index ace479ab273..7b6d3716efc 100644 --- a/arch/v850/Kconfig +++ b/arch/v850/Kconfig @@ -53,6 +53,9 @@ config ARCH_HAS_ILOG2_U64 bool default n +config ARCH_SUPPORTS_AOUT + def_bool y + # Turn off some random 386 crap that can affect device config config ISA bool @@ -212,6 +215,13 @@ menu "Processor type and features" bool default !V850E_CACHE && !V850E2_CACHE + # HZ depends on the platform + config HZ + int + default 24 if V850E_SIM || V850E2_SIM85E2 + default 122 if V850E2_FPGA85E2C + default 100 + #### Misc config config ROM_KERNEL diff --git a/arch/v850/kernel/procfs.c b/arch/v850/kernel/procfs.c index e6f9d060ad5..e433cde789b 100644 --- a/arch/v850/kernel/procfs.c +++ b/arch/v850/kernel/procfs.c @@ -59,7 +59,7 @@ static void cpuinfo_stop (struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = cpuinfo_start, .next = cpuinfo_next, .stop = cpuinfo_stop, diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 9d0acedf5f3..65a70b777c1 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -136,6 +136,9 @@ config AUDIT_ARCH bool default X86_64 +config ARCH_SUPPORTS_AOUT + def_bool y + # Use the generic interrupt handling code in kernel/irq/: config GENERIC_HARDIRQS bool @@ -1577,7 +1580,7 @@ config IA32_EMULATION config IA32_AOUT tristate "IA32 a.out support" - depends on IA32_EMULATION + depends on IA32_EMULATION && ARCH_SUPPORTS_AOUT help Support old a.out binaries in the 32bit emulation. diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index dabdbeff1f7..a7d50a547dc 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -23,7 +23,6 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/utsname.h> #include <linux/delay.h> @@ -539,55 +538,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, return err; } -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - u16 gs; - -/* changed the size calculations - should hopefully work better. lbt */ - dump->magic = CMAGIC; - dump->start_code = 0; - dump->start_stack = regs->sp & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; - dump->u_dsize -= dump->u_tsize; - dump->u_ssize = 0; - dump->u_debugreg[0] = current->thread.debugreg0; - dump->u_debugreg[1] = current->thread.debugreg1; - dump->u_debugreg[2] = current->thread.debugreg2; - dump->u_debugreg[3] = current->thread.debugreg3; - dump->u_debugreg[4] = 0; - dump->u_debugreg[5] = 0; - dump->u_debugreg[6] = current->thread.debugreg6; - dump->u_debugreg[7] = current->thread.debugreg7; - - if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - - dump->regs.bx = regs->bx; - dump->regs.cx = regs->cx; - dump->regs.dx = regs->dx; - dump->regs.si = regs->si; - dump->regs.di = regs->di; - dump->regs.bp = regs->bp; - dump->regs.ax = regs->ax; - dump->regs.ds = (u16)regs->ds; - dump->regs.es = (u16)regs->es; - dump->regs.fs = (u16)regs->fs; - savesegment(gs,gs); - dump->regs.orig_ax = regs->orig_ax; - dump->regs.ip = regs->ip; - dump->regs.cs = (u16)regs->cs; - dump->regs.flags = regs->flags; - dump->regs.sp = regs->sp; - dump->regs.ss = (u16)regs->ss; - - dump->u_fpvalid = dump_fpu (regs, &dump->i387); -} -EXPORT_SYMBOL(dump_thread); - #ifdef CONFIG_SECCOMP static void hard_disable_TSC(void) { diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 137a86171c3..b0cc8f0136d 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -26,7 +26,6 @@ #include <linux/smp.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/utsname.h> #include <linux/delay.h> diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index a49f5f734a5..c0d8208af12 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -15,7 +15,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/ioport.h> #include <linux/delay.h> diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 0380795121a..c737849e2ef 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -77,7 +77,7 @@ unsigned long __init native_calculate_cpu_khz(void) reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i); } local_irq_save(flags); - /* start meauring cycles, incrementing from 0 */ + /* start measuring cycles, incrementing from 0 */ wrmsrl(MSR_K7_PERFCTR0 + i, 0); wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76); rdtscl(tsc_start); diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c index 6c1914622a8..73aba712520 100644 --- a/arch/x86/mm/pgtable_32.c +++ b/arch/x86/mm/pgtable_32.c @@ -183,7 +183,7 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *pte; @@ -192,6 +192,8 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #else pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0); #endif + if (pte) + pgtable_page_ctor(pte); return pte; } @@ -365,6 +367,7 @@ void check_pgt_cache(void) void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte) { + pgtable_page_dtor(pte); paravirt_release_pt(page_to_pfn(pte)); tlb_remove_page(tlb, pte); } diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 5d5546ce88f..fd36764d7fb 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -49,6 +49,10 @@ config ARCH_HAS_ILOG2_U64 config NO_IOPORT def_bool y +config HZ + int + default 100 + source "init/Kconfig" menu "Processor type and features" diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 2e8d398cf19..b80f2cb1b4f 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -469,7 +469,7 @@ c_stop(struct seq_file *f, void *v) { } -struct seq_operations cpuinfo_op = +const struct seq_operations cpuinfo_op = { start: c_start, next: c_next, |