mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
436
arch/cris/arch-v32/drivers/Kconfig
Normal file
436
arch/cris/arch-v32/drivers/Kconfig
Normal file
|
@ -0,0 +1,436 @@
|
|||
if ETRAX_ARCH_V32
|
||||
|
||||
config ETRAX_ETHERNET
|
||||
bool "Ethernet support"
|
||||
depends on ETRAX_ARCH_V32 && NETDEVICES
|
||||
select MII
|
||||
help
|
||||
This option enables the ETRAX FS built-in 10/100Mbit Ethernet
|
||||
controller.
|
||||
|
||||
config ETRAX_NO_PHY
|
||||
bool "PHY not present"
|
||||
depends on ETRAX_ETHERNET
|
||||
help
|
||||
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 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
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser0 (ttyS0)
|
||||
Normally you want this on. You can control what DMA channels to use
|
||||
if you do not need DMA to something else.
|
||||
ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
|
||||
|
||||
config ETRAX_SERIAL_PORT1
|
||||
bool "Serial port 1 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser1 (ttyS1).
|
||||
|
||||
config ETRAX_SERIAL_PORT2
|
||||
bool "Serial port 2 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser2 (ttyS2).
|
||||
|
||||
config ETRAX_SERIAL_PORT3
|
||||
bool "Serial port 3 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser3 (ttyS3).
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL
|
||||
bool "Synchronous serial-port support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
Enables the ETRAX FS synchronous serial 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
|
||||
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.
|
||||
|
||||
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_BLOCK
|
||||
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
|
||||
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
|
||||
default "65536"
|
||||
help
|
||||
Byte-offset of the partition table in the first flash chip.
|
||||
The default value is 64kB and should not be changed unless
|
||||
you know exactly what you are doing. The only valid reason
|
||||
for changing this is when the flash block size is bigger
|
||||
than 64kB (e.g. when using two parallel 16 bit flashes).
|
||||
|
||||
config ETRAX_NANDFLASH
|
||||
bool "NAND flash support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select MTD_NAND
|
||||
select MTD_NAND_IDS
|
||||
help
|
||||
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 enables the I2C driver used by e.g. the RTC driver.
|
||||
|
||||
config ETRAX_V32_I2C_DATA_PORT
|
||||
string "I2C data pin"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
The pin to use for I2C data.
|
||||
|
||||
config ETRAX_V32_I2C_CLK_PORT
|
||||
string "I2C clock pin"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
The pin to use for I2C clock.
|
||||
|
||||
config ETRAX_GPIO
|
||||
bool "GPIO support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
---help---
|
||||
Enables the ETRAX general port device (major 120, minors 0-4).
|
||||
You can use this driver to access the general port bits. It supports
|
||||
these ioctl's:
|
||||
#include <linux/etraxgpio.h>
|
||||
fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
|
||||
err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val);
|
||||
Remember that you need to setup the port directions appropriately in
|
||||
the General configuration.
|
||||
|
||||
config ETRAX_VIRTUAL_GPIO
|
||||
bool "Virtual GPIO support"
|
||||
depends on ETRAX_GPIO
|
||||
help
|
||||
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" 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 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" 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.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PB_CHANGEABLE_DIR
|
||||
hex "PB user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
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 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" 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.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PC_CHANGEABLE_DIR
|
||||
hex "PC user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
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 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" 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.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PD_CHANGEABLE_DIR
|
||||
hex "PD user changeable dir mask"
|
||||
depends on ETRAX_GPIO && ETRAXFS
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PD
|
||||
that a user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PD_CHANGEABLE_BITS
|
||||
hex "PD user changeable bits mask"
|
||||
depends on ETRAX_GPIO && ETRAXFS
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PD
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PE_CHANGEABLE_DIR
|
||||
hex "PE user changeable dir mask"
|
||||
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 direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PE_CHANGEABLE_BITS
|
||||
hex "PE user changeable bits mask"
|
||||
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
|
||||
help
|
||||
Enabled the ETRAX Cardbus driver.
|
||||
|
||||
config PCI
|
||||
bool
|
||||
depends on ETRAX_CARDBUS
|
||||
default y
|
||||
select HAVE_GENERIC_DMA_COHERENT
|
||||
|
||||
config ETRAX_IOP_FW_LOAD
|
||||
tristate "IO-processor hotplug firmware loading support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select FW_LOADER
|
||||
help
|
||||
Enables IO-processor hotplug firmware loading support.
|
||||
|
||||
config ETRAX_STREAMCOPROC
|
||||
tristate "Stream co-processor driver enabled"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
This option enables a driver for the stream co-processor
|
||||
for cryptographic operations.
|
||||
|
||||
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
|
||||
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
|
||||
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_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_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.)
|
||||
|
||||
endif
|
13
arch/cris/arch-v32/drivers/Makefile
Normal file
13
arch/cris/arch-v32/drivers/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o
|
||||
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.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_I2C) += i2c.o
|
||||
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
|
||||
obj-$(CONFIG_PCI) += pci/
|
||||
obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o
|
619
arch/cris/arch-v32/drivers/axisflashmap.c
Normal file
619
arch/cris/arch-v32/drivers/axisflashmap.c
Normal file
|
@ -0,0 +1,619 @@
|
|||
/*
|
||||
* Physical mapping layer for MTD using the Axis partitiontable format
|
||||
*
|
||||
* Copyright (c) 2001-2007 Axis Communications AB
|
||||
*
|
||||
* This file is under the GPL.
|
||||
*
|
||||
* First partition is always sector 0 regardless of if we find a partitiontable
|
||||
* or not. In the start of the next sector, there can be a partitiontable that
|
||||
* tells us what other partitions to define. If there isn't, we use a default
|
||||
* partition split defined below.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mtd/concat.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/mtdram.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/axisflashmap.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
#define MEM_CSE0_SIZE (0x04000000)
|
||||
#define MEM_CSE1_SIZE (0x04000000)
|
||||
|
||||
#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 __u32
|
||||
#endif
|
||||
|
||||
/* From head.S */
|
||||
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;
|
||||
|
||||
/* Map driver functions. */
|
||||
|
||||
static map_word flash_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word tmp;
|
||||
tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void flash_copy_from(struct map_info *map, void *to,
|
||||
unsigned long from, ssize_t len)
|
||||
{
|
||||
memcpy(to, (void *)(map->map_priv_1 + from), len);
|
||||
}
|
||||
|
||||
static void flash_write(struct map_info *map, map_word d, unsigned long adr)
|
||||
{
|
||||
*(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* The map for chip select e0.
|
||||
*
|
||||
* We run into tricky coherence situations if we mix cached with uncached
|
||||
* accesses to we only use the uncached version here.
|
||||
*
|
||||
* The size field is the total size where the flash chips may be mapped on the
|
||||
* chip select. MTD probes should find all devices there and it does not matter
|
||||
* if there are unmapped gaps or aliases (mirrors of flash devices). The MTD
|
||||
* probes will ignore them.
|
||||
*
|
||||
* The start address in map_priv_1 is in virtual memory so we cannot use
|
||||
* MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start
|
||||
* address of cse0.
|
||||
*/
|
||||
static struct map_info map_cse0 = {
|
||||
.name = "cse0",
|
||||
.size = MEM_CSE0_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR
|
||||
};
|
||||
|
||||
/*
|
||||
* The map for chip select e1.
|
||||
*
|
||||
* If there was a gap between cse0 and cse1, map_priv_1 would get the wrong
|
||||
* address, but there isn't.
|
||||
*/
|
||||
static struct map_info map_cse1 = {
|
||||
.name = "cse1",
|
||||
.size = MEM_CSE1_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
#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] = {
|
||||
{
|
||||
.name = "part0",
|
||||
.size = CONFIG_ETRAX_PTABLE_SECTOR,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part1",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part2",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part3",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part4",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part5",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part6",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
/* Auxiliary 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).
|
||||
*/
|
||||
static struct mtd_info *probe_cs(struct map_info *map_cs)
|
||||
{
|
||||
struct mtd_info *mtd_cs = NULL;
|
||||
|
||||
printk(KERN_INFO
|
||||
"%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
|
||||
map_cs->name, map_cs->size, map_cs->map_priv_1);
|
||||
|
||||
#ifdef CONFIG_MTD_CFI
|
||||
mtd_cs = do_map_probe("cfi_probe", map_cs);
|
||||
#endif
|
||||
#ifdef CONFIG_MTD_JEDECPROBE
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* to do that anyway and you can put totally different chips on cse0 and cse1
|
||||
* so it isn't really much of a restriction.
|
||||
*/
|
||||
extern struct mtd_info* __init crisv32_nand_flash_probe (void);
|
||||
static struct mtd_info *flash_probe(void)
|
||||
{
|
||||
struct mtd_info *mtd_cse0;
|
||||
struct mtd_info *mtd_cse1;
|
||||
struct mtd_info *mtd_total;
|
||||
struct mtd_info *mtds[2];
|
||||
int count = 0;
|
||||
|
||||
if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL)
|
||||
mtds[count++] = mtd_cse0;
|
||||
if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL)
|
||||
mtds[count++] = mtd_cse1;
|
||||
|
||||
if (!mtd_cse0 && !mtd_cse1) {
|
||||
/* No chip found. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
/* 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
|
||||
* flash chips are slow, the overhead is relatively small.
|
||||
* So we use the MTD concatenation layer instead of further
|
||||
* complicating the probing procedure.
|
||||
*/
|
||||
mtd_total = mtd_concat_create(mtds, count, "cse0+cse1");
|
||||
if (!mtd_total) {
|
||||
printk(KERN_ERR "%s and %s: Concatenation failed!\n",
|
||||
map_cse0.name, map_cse1.name);
|
||||
|
||||
/* The best we can do now is to only use what we found
|
||||
* at cse0. */
|
||||
mtd_total = mtd_cse0;
|
||||
map_destroy(mtd_cse1);
|
||||
}
|
||||
} else
|
||||
mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1;
|
||||
|
||||
return mtd_total;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *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 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
|
||||
|
||||
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;
|
||||
|
||||
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 (!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");
|
||||
}
|
||||
|
||||
#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 = 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) {
|
||||
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
|
||||
}
|
||||
|
||||
if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
|
||||
&& (ptable_head->size <
|
||||
(MAX_PARTITIONS * sizeof(struct partitiontable_entry) +
|
||||
PARTITIONTABLE_END_MARKER_SIZE))
|
||||
&& (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) +
|
||||
ptable_head->size -
|
||||
PARTITIONTABLE_END_MARKER_SIZE)
|
||||
== PARTITIONTABLE_END_MARKER)) {
|
||||
/* Looks like a start, sane length and end of a
|
||||
* partition table, lets check csum etc.
|
||||
*/
|
||||
struct partitiontable_entry *max_addr =
|
||||
(struct partitiontable_entry *)
|
||||
((unsigned long)ptable_head + sizeof(*ptable_head) +
|
||||
ptable_head->size);
|
||||
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));
|
||||
|
||||
/* Lets be PARANOID, and check the checksum. */
|
||||
p = (unsigned char*) ptable;
|
||||
|
||||
while (p <= (unsigned char*)max_addr) {
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
}
|
||||
ptable_ok = (csum == ptable_head->checksum);
|
||||
|
||||
/* Read the entries and use/show the info. */
|
||||
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 with 0xffffffff.
|
||||
*/
|
||||
while (ptable_ok
|
||||
&& ptable->offset != PARTITIONTABLE_END_MARKER
|
||||
&& ptable < max_addr
|
||||
&& pidx < MAX_PARTITIONS - 1) {
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decide whether to use default partition table. */
|
||||
/* Only use default table if we actually have a device (main_mtd) */
|
||||
|
||||
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;
|
||||
ram_rootfs_partition = -1;
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
|
||||
if (main_mtd) {
|
||||
main_partition.size = main_mtd->size;
|
||||
err = mtd_device_register(main_mtd, &main_partition, 1);
|
||||
if (err)
|
||||
panic("axisflashmap: Could not initialize "
|
||||
"partition for whole main mtd device!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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 = mtd_device_register(main_mtd, &partition[part],
|
||||
1);
|
||||
if (err)
|
||||
panic("axisflashmap: Could not add mtd "
|
||||
"partition %d\n", part);
|
||||
}
|
||||
}
|
||||
|
||||
if (aux_mtd) {
|
||||
aux_partition.size = aux_mtd->size;
|
||||
err = mtd_device_register(aux_mtd, &aux_partition, 1);
|
||||
if (err)
|
||||
panic("axisflashmap: Could not initialize "
|
||||
"aux mtd device!\n");
|
||||
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This adds the above to the kernels init-call chain. */
|
||||
module_init(init_axis_flash);
|
||||
|
||||
EXPORT_SYMBOL(axisflash_mtd);
|
3536
arch/cris/arch-v32/drivers/cryptocop.c
Normal file
3536
arch/cris/arch-v32/drivers/cryptocop.c
Normal file
File diff suppressed because it is too large
Load diff
751
arch/cris/arch-v32/drivers/i2c.c
Normal file
751
arch/cris/arch-v32/drivers/i2c.c
Normal file
|
@ -0,0 +1,751 @@
|
|||
/*!***************************************************************************
|
||||
*!
|
||||
*! FILE NAME : i2c.c
|
||||
*!
|
||||
*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
|
||||
*! 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.
|
||||
*| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now
|
||||
*| generates nack on last received byte,
|
||||
*| instead of ack.
|
||||
*| i2c_getack changed data level while clock
|
||||
*| was high, causing DS75 to see a stop condition
|
||||
*!
|
||||
*! ---------------------------------------------------------------------------
|
||||
*!
|
||||
*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
*!***************************************************************************/
|
||||
|
||||
/****************** INCLUDE FILES SECTION ***********************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <asm/etraxi2c.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
/****************** I2C DEFINITION SECTION *************************/
|
||||
|
||||
#define D(x)
|
||||
|
||||
#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
|
||||
static DEFINE_MUTEX(i2c_mutex);
|
||||
static const char i2c_name[] = "i2c";
|
||||
|
||||
#define CLOCK_LOW_TIME 8
|
||||
#define CLOCK_HIGH_TIME 8
|
||||
#define START_CONDITION_HOLD_TIME 8
|
||||
#define STOP_CONDITION_HOLD_TIME 8
|
||||
#define ENABLE_OUTPUT 0x01
|
||||
#define ENABLE_INPUT 0x00
|
||||
#define I2C_CLOCK_HIGH 1
|
||||
#define I2C_CLOCK_LOW 0
|
||||
#define I2C_DATA_HIGH 1
|
||||
#define I2C_DATA_LOW 0
|
||||
|
||||
#define i2c_enable()
|
||||
#define i2c_disable()
|
||||
|
||||
/* enable or disable output-enable, to select output or input on the i2c bus */
|
||||
|
||||
#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
|
||||
#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
|
||||
|
||||
/* control the i2c clock and data signals */
|
||||
|
||||
#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
|
||||
#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
|
||||
|
||||
/* read a bit from the i2c interface */
|
||||
|
||||
#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
|
||||
|
||||
#define i2c_delay(usecs) udelay(usecs)
|
||||
|
||||
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
|
||||
|
||||
/****************** VARIABLE SECTION ************************************/
|
||||
|
||||
static struct crisv32_iopin cris_i2c_clk;
|
||||
static struct crisv32_iopin cris_i2c_data;
|
||||
|
||||
/****************** FUNCTION DEFINITION SECTION *************************/
|
||||
|
||||
|
||||
/* generate i2c start condition */
|
||||
|
||||
void
|
||||
i2c_start(void)
|
||||
{
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(START_CONDITION_HOLD_TIME);
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
}
|
||||
|
||||
/* generate i2c stop condition */
|
||||
|
||||
void
|
||||
i2c_stop(void)
|
||||
{
|
||||
i2c_dir_out();
|
||||
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(STOP_CONDITION_HOLD_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* write a byte to the i2c interface */
|
||||
|
||||
void
|
||||
i2c_outbyte(unsigned char x)
|
||||
{
|
||||
int i;
|
||||
|
||||
i2c_dir_out();
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (x & 0x80) {
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
} else {
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
}
|
||||
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
x <<= 1;
|
||||
}
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* read a byte from the i2c interface */
|
||||
|
||||
unsigned char
|
||||
i2c_inbyte(void)
|
||||
{
|
||||
unsigned char aBitByte = 0;
|
||||
int i;
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
for (i = 1; i < 8; i++) {
|
||||
aBitByte <<= 1;
|
||||
/* Clock pulse */
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
}
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
|
||||
/*
|
||||
* we leave the clock low, getbyte is usually followed
|
||||
* by sendack/nack, they assume the clock to be low
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
return aBitByte;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_getack
|
||||
*#
|
||||
*# DESCRIPTION : checks if ack was received from ic2
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
i2c_getack(void)
|
||||
{
|
||||
int ack = 1;
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* Release data bus by setting
|
||||
* data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* generate ACK clock pulse
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
#if 0
|
||||
/*
|
||||
* Use PORT PB instead of I2C
|
||||
* for input. (I2C not working)
|
||||
*/
|
||||
i2c_clk(1);
|
||||
i2c_data(1);
|
||||
/*
|
||||
* switch off I2C
|
||||
*/
|
||||
i2c_data(1);
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* now wait for ack
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
/*
|
||||
* check for ack
|
||||
*/
|
||||
if (i2c_getbit())
|
||||
ack = 0;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
if (!ack) {
|
||||
if (!i2c_getbit()) /* receiver pulld SDA low */
|
||||
ack = 1;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
}
|
||||
|
||||
/*
|
||||
* our clock is high now, make sure data is low
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* end clock pulse
|
||||
*/
|
||||
i2c_enable();
|
||||
i2c_dir_out();
|
||||
#endif
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* remove ACK clock pulse
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
return ack;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: I2C::sendAck
|
||||
*#
|
||||
*# DESCRIPTION : Send ACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set ack pulse high
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/6);
|
||||
/*
|
||||
* reset data out
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_sendnack
|
||||
*#
|
||||
*# DESCRIPTION : Sends NACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendnack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# 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
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
int
|
||||
i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
||||
unsigned char theValue)
|
||||
{
|
||||
int error, cntr = 3;
|
||||
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;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 2;
|
||||
/*
|
||||
* send register register data
|
||||
*/
|
||||
i2c_outbyte(theValue);
|
||||
/*
|
||||
* 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_readreg
|
||||
*#
|
||||
*# DESCRIPTION : Reads a value from the decoder registers.
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
unsigned char
|
||||
i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
||||
{
|
||||
unsigned char b = 0;
|
||||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i2c_lock, flags);
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* generate start condition
|
||||
*/
|
||||
i2c_start();
|
||||
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte((theSlave & 0xfe));
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 2;
|
||||
/*
|
||||
* repeat start condition
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_start();
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte(theSlave | 0x01);
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 4;
|
||||
/*
|
||||
* fetch register
|
||||
*/
|
||||
b = i2c_inbyte();
|
||||
/*
|
||||
* 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 b;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main device API. ioctl's to write or read to/from i2c registers.
|
||||
*/
|
||||
|
||||
static long
|
||||
i2c_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case I2C_WRITEREG:
|
||||
/* write to an i2c slave */
|
||||
D(printk("i2cw %d %d %d\n",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg)));
|
||||
|
||||
mutex_lock(&i2c_mutex);
|
||||
ret = i2c_writereg(I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg));
|
||||
mutex_unlock(&i2c_mutex);
|
||||
return ret;
|
||||
|
||||
case I2C_READREG:
|
||||
{
|
||||
unsigned char val;
|
||||
/* read from an i2c slave */
|
||||
D(printk("i2cr %d %d ",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg)));
|
||||
mutex_lock(&i2c_mutex);
|
||||
val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
|
||||
mutex_unlock(&i2c_mutex);
|
||||
D(printk("= %d\n", val));
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations i2c_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = i2c_ioctl,
|
||||
.open = i2c_open,
|
||||
.release = i2c_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int __init i2c_init(void)
|
||||
{
|
||||
static int res;
|
||||
static int first = 1;
|
||||
|
||||
if (!first)
|
||||
return res;
|
||||
|
||||
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) {
|
||||
printk(KERN_ERR "i2c: couldn't get a major number.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
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_register);
|
||||
|
||||
/****************** END OF FILE i2c.c ********************************/
|
17
arch/cris/arch-v32/drivers/i2c.h
Normal file
17
arch/cris/arch-v32/drivers/i2c.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Low level I2C */
|
||||
void i2c_start(void);
|
||||
void i2c_stop(void);
|
||||
void i2c_outbyte(unsigned char x);
|
||||
unsigned char i2c_inbyte(void);
|
||||
int i2c_getack(void);
|
||||
void i2c_sendack(void);
|
230
arch/cris/arch-v32/drivers/iop_fw_load.c
Normal file
230
arch/cris/arch-v32/drivers/iop_fw_load.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Firmware loader for ETRAX FS IO-Processor
|
||||
*
|
||||
* Copyright (C) 2004 Axis Communications AB
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.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
|
||||
|
||||
#error "This driver is broken with regard to its driver core usage."
|
||||
#error "Please contact <greg@kroah.com> for details on how to fix it properly."
|
||||
|
||||
static struct device iop_spu_device[2] = {
|
||||
{ .init_name = "iop-spu0", },
|
||||
{ .init_name = "iop-spu1", },
|
||||
};
|
||||
|
||||
static struct device iop_mpu_device = {
|
||||
.init_name = "iop-mpu",
|
||||
};
|
||||
|
||||
static int wait_mpu_idle(void)
|
||||
{
|
||||
reg_iop_mpu_r_stat mpu_stat;
|
||||
unsigned int timeout = IOP_TIMEOUT;
|
||||
|
||||
do {
|
||||
mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
|
||||
} while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
|
||||
{
|
||||
reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
|
||||
.wr_spu0_mem = regk_iop_sw_cpu_no,
|
||||
.wr_spu1_mem = regk_iop_sw_cpu_no,
|
||||
.size = 4,
|
||||
.cmd = regk_iop_sw_cpu_reg_copy,
|
||||
.keep_owner = regk_iop_sw_cpu_yes
|
||||
};
|
||||
reg_iop_spu_rw_ctrl spu_ctrl = {
|
||||
.en = regk_iop_spu_no,
|
||||
.fsm = regk_iop_spu_no,
|
||||
};
|
||||
reg_iop_sw_cpu_r_mc_stat mc_stat;
|
||||
const struct firmware *fw_entry;
|
||||
u32 *data;
|
||||
unsigned int timeout;
|
||||
int retval, i;
|
||||
|
||||
if (spu_inst > 1)
|
||||
return -ENODEV;
|
||||
|
||||
/* get firmware */
|
||||
retval = request_firmware(&fw_entry,
|
||||
fw_name,
|
||||
&iop_spu_device[spu_inst]);
|
||||
if (retval != 0)
|
||||
{
|
||||
printk(KERN_ERR
|
||||
"iop_load_spu: Failed to load firmware \"%s\"\n",
|
||||
fw_name);
|
||||
return retval;
|
||||
}
|
||||
data = (u32 *) fw_entry->data;
|
||||
|
||||
/* acquire ownership of memory controller */
|
||||
switch (spu_inst) {
|
||||
case 0:
|
||||
mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
|
||||
REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
|
||||
break;
|
||||
case 1:
|
||||
mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
|
||||
REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
|
||||
break;
|
||||
}
|
||||
timeout = IOP_TIMEOUT;
|
||||
do {
|
||||
REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
|
||||
mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
|
||||
} while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Timeout waiting to acquire MC\n");
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write to SPU memory */
|
||||
for (i = 0; i < (fw_entry->size/4); i++) {
|
||||
switch (spu_inst) {
|
||||
case 0:
|
||||
REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
|
||||
break;
|
||||
case 1:
|
||||
REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
|
||||
break;
|
||||
}
|
||||
REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
|
||||
data++;
|
||||
}
|
||||
|
||||
/* release ownership of memory controller */
|
||||
(void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);
|
||||
|
||||
out:
|
||||
release_firmware(fw_entry);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int iop_fw_load_mpu(unsigned char *fw_name)
|
||||
{
|
||||
const unsigned int start_addr = 0;
|
||||
reg_iop_mpu_rw_ctrl mpu_ctrl;
|
||||
const struct firmware *fw_entry;
|
||||
u32 *data;
|
||||
int retval, i;
|
||||
|
||||
/* get firmware */
|
||||
retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
|
||||
if (retval != 0)
|
||||
{
|
||||
printk(KERN_ERR
|
||||
"iop_load_spu: Failed to load firmware \"%s\"\n",
|
||||
fw_name);
|
||||
return retval;
|
||||
}
|
||||
data = (u32 *) fw_entry->data;
|
||||
|
||||
/* disable MPU */
|
||||
mpu_ctrl.en = regk_iop_mpu_no;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
|
||||
/* put start address in R0 */
|
||||
REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr);
|
||||
/* write to memory by executing 'SWX i, 4, R0' for each word */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0));
|
||||
for (i = 0; i < (fw_entry->size / 4); i++) {
|
||||
REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data);
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
data++;
|
||||
}
|
||||
|
||||
out:
|
||||
release_firmware(fw_entry);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int iop_start_mpu(unsigned int start_addr)
|
||||
{
|
||||
reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes };
|
||||
int retval;
|
||||
|
||||
/* disable MPU */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT());
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* set PC and wait for it to bite */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr));
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* make sure the MPU starts executing with interrupts disabled */
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI());
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* enable MPU */
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __init iop_fw_load_init(void)
|
||||
{
|
||||
#if 0
|
||||
/*
|
||||
* static struct devices can not be added directly to sysfs by ignoring
|
||||
* the driver model infrastructure. To fix this properly, please use
|
||||
* the platform_bus to register these devices to be able to properly
|
||||
* use the firmware infrastructure.
|
||||
*/
|
||||
device_initialize(&iop_spu_device[0]);
|
||||
kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0");
|
||||
kobject_add(&iop_spu_device[0].kobj);
|
||||
device_initialize(&iop_spu_device[1]);
|
||||
kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1");
|
||||
kobject_add(&iop_spu_device[1].kobj);
|
||||
device_initialize(&iop_mpu_device);
|
||||
kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
|
||||
kobject_add(&iop_mpu_device.kobj);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit iop_fw_load_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(iop_fw_load_init);
|
||||
module_exit(iop_fw_load_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
EXPORT_SYMBOL(iop_fw_load_spu);
|
||||
EXPORT_SYMBOL(iop_fw_load_mpu);
|
||||
EXPORT_SYMBOL(iop_start_mpu);
|
6
arch/cris/arch-v32/drivers/mach-a3/Makefile
Normal file
6
arch/cris/arch-v32/drivers/mach-a3/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
|
||||
obj-$(CONFIG_ETRAX_GPIO) += gpio.o
|
999
arch/cris/arch-v32/drivers/mach-a3/gpio.c
Normal file
999
arch/cris/arch-v32/drivers/mach-a3/gpio.c
Normal file
|
@ -0,0 +1,999 @@
|
|||
/*
|
||||
* 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 <linux/mutex.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/irq.h>
|
||||
#include <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 DEFINE_MUTEX(gpio_mutex);
|
||||
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 long gpio_ioctl(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;
|
||||
|
||||
mutex_lock(&gpio_mutex);
|
||||
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);
|
||||
}
|
||||
|
||||
mutex_unlock(&gpio_mutex);
|
||||
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 long gpio_ioctl_unlocked(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;
|
||||
}
|
||||
|
||||
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
mutex_lock(&gpio_mutex);
|
||||
ret = gpio_ioctl_unlocked(file, cmd, arg);
|
||||
mutex_unlock(&gpio_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#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_unlock_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_unlock_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,
|
||||
.unlocked_ioctl = gpio_ioctl,
|
||||
.write = gpio_write,
|
||||
.open = gpio_open,
|
||||
.release = gpio_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
#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, "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);
|
180
arch/cris/arch-v32/drivers/mach-a3/nandflash.c
Normal file
180
arch/cris/arch-v32/drivers/mach-a3/nandflash.c
Normal file
|
@ -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 <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->bbt_options = NAND_BBT_USE_FLASH; */
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(crisv32_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
return crisv32_mtd;
|
||||
|
||||
out_mtd:
|
||||
kfree(wrapper);
|
||||
return NULL;
|
||||
}
|
||||
|
6
arch/cris/arch-v32/drivers/mach-fs/Makefile
Normal file
6
arch/cris/arch-v32/drivers/mach-fs/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
|
||||
obj-$(CONFIG_ETRAX_GPIO) += gpio.o
|
979
arch/cris/arch-v32/drivers/mach-fs/gpio.c
Normal file
979
arch/cris/arch-v32/drivers/mach-fs/gpio.c
Normal file
|
@ -0,0 +1,979 @@
|
|||
/*
|
||||
* ETRAX CRISv32 general port I/O device
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <linux/mutex.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/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
|
||||
* pc 18 bits
|
||||
* pd 18 bits
|
||||
* pe 18 bits
|
||||
* each port has a rw_px_dout, r_px_din and rw_px_oe register.
|
||||
*/
|
||||
|
||||
#define GPIO_MAJOR 120 /* experimental MAJOR number */
|
||||
|
||||
#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 DEFINE_MUTEX(gpio_mutex);
|
||||
static char gpio_name[] = "etrax gpio";
|
||||
|
||||
#if 0
|
||||
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 long gpio_ioctl(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);
|
||||
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;
|
||||
};
|
||||
|
||||
/* linked list of alarms to check for */
|
||||
|
||||
static struct gpio_private *alarmlist;
|
||||
|
||||
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)
|
||||
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),
|
||||
GIO_REG_WR_ADDR(rw_pb_dout),
|
||||
&led_dummy,
|
||||
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] = {
|
||||
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),
|
||||
GIO_REG_RD_ADDR(r_pe_din),
|
||||
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
|
||||
&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,
|
||||
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] = {
|
||||
CONFIG_ETRAX_PA_CHANGEABLE_BITS,
|
||||
CONFIG_ETRAX_PB_CHANGEABLE_BITS,
|
||||
0,
|
||||
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] = {
|
||||
GIO_REG_WR_ADDR(rw_pa_oe),
|
||||
GIO_REG_WR_ADDR(rw_pb_oe),
|
||||
&led_dummy,
|
||||
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, struct poll_table_struct *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
struct gpio_private *priv = file->private_data;
|
||||
unsigned long data;
|
||||
poll_wait(file, &priv->alarm_wq, wait);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
reg_gio_rw_intr_cfg intr_cfg;
|
||||
unsigned long tmp;
|
||||
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));
|
||||
/* 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))
|
||||
intr_cfg.pa0 = regk_gio_hi;
|
||||
if (tmp & (1 << 1))
|
||||
intr_cfg.pa1 = regk_gio_hi;
|
||||
if (tmp & (1 << 2))
|
||||
intr_cfg.pa2 = regk_gio_hi;
|
||||
if (tmp & (1 << 3))
|
||||
intr_cfg.pa3 = regk_gio_hi;
|
||||
if (tmp & (1 << 4))
|
||||
intr_cfg.pa4 = regk_gio_hi;
|
||||
if (tmp & (1 << 5))
|
||||
intr_cfg.pa5 = regk_gio_hi;
|
||||
if (tmp & (1 << 6))
|
||||
intr_cfg.pa6 = regk_gio_hi;
|
||||
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))
|
||||
intr_cfg.pa0 = regk_gio_lo;
|
||||
if (tmp & (1 << 1))
|
||||
intr_cfg.pa1 = regk_gio_lo;
|
||||
if (tmp & (1 << 2))
|
||||
intr_cfg.pa2 = regk_gio_lo;
|
||||
if (tmp & (1 << 3))
|
||||
intr_cfg.pa3 = regk_gio_lo;
|
||||
if (tmp & (1 << 4))
|
||||
intr_cfg.pa4 = regk_gio_lo;
|
||||
if (tmp & (1 << 5))
|
||||
intr_cfg.pa5 = regk_gio_lo;
|
||||
if (tmp & (1 << 6))
|
||||
intr_cfg.pa6 = regk_gio_lo;
|
||||
if (tmp & (1 << 7))
|
||||
intr_cfg.pa7 = regk_gio_lo;
|
||||
|
||||
REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
|
||||
local_irq_restore(flags);
|
||||
} else if (priv->minor <= GPIO_MINOR_E)
|
||||
data = *data_in[priv->minor];
|
||||
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;
|
||||
}
|
||||
|
||||
int etrax_gpio_wake_up_check(void)
|
||||
{
|
||||
struct gpio_private *priv;
|
||||
unsigned long data = 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(KERN_DEBUG
|
||||
"etrax_gpio_wake_up_check %i\n", priv->minor));
|
||||
wake_up_interruptible(&priv->alarm_wq);
|
||||
ret = 1;
|
||||
}
|
||||
priv = priv->next;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
gpio_poll_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
if (gpio_some_alarms)
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
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);
|
||||
tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
|
||||
|
||||
/* Find those that we have enabled */
|
||||
spin_lock(&alarm_lock);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (gpio_some_alarms)
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t gpio_write(struct file *file, const char *buf, size_t count,
|
||||
loff_t *off)
|
||||
{
|
||||
struct gpio_private *priv = file->private_data;
|
||||
unsigned char data, clk_mask, data_mask, write_msb;
|
||||
unsigned long flags;
|
||||
unsigned long shadow;
|
||||
volatile unsigned long *port;
|
||||
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 (!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)
|
||||
return -EPERM;
|
||||
write_msb = priv->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--) {
|
||||
local_irq_save(flags);
|
||||
shadow = *port;
|
||||
*port = shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*port = shadow |= data_mask;
|
||||
else
|
||||
*port = shadow &= ~data_mask;
|
||||
/* For FPGA: min 5.0ns (DCC) before CCLK high */
|
||||
*port = shadow |= clk_mask;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i <= 7; i++) {
|
||||
local_irq_save(flags);
|
||||
shadow = *port;
|
||||
*port = shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*port = shadow |= data_mask;
|
||||
else
|
||||
*port = shadow &= ~data_mask;
|
||||
/* For FPGA: min 5.0ns (DCC) before CCLK high */
|
||||
*port = shadow |= clk_mask;
|
||||
local_irq_restore(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)
|
||||
return -EINVAL;
|
||||
|
||||
priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&gpio_mutex);
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
priv->minor = p;
|
||||
|
||||
/* initialize the io/alarm struct */
|
||||
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
priv->highalarm = 0;
|
||||
priv->lowalarm = 0;
|
||||
init_waitqueue_head(&priv->alarm_wq);
|
||||
|
||||
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);
|
||||
|
||||
mutex_unlock(&gpio_mutex);
|
||||
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;
|
||||
unsigned long some_alarms;
|
||||
|
||||
/* unlink from alarmlist and free the private structure */
|
||||
|
||||
spin_lock_irq(&alarm_lock);
|
||||
p = alarmlist;
|
||||
todel = filp->private_data;
|
||||
|
||||
if (p == todel) {
|
||||
alarmlist = todel->next;
|
||||
} else {
|
||||
while (p->next != todel)
|
||||
p = p->next;
|
||||
p->next = todel->next;
|
||||
}
|
||||
|
||||
kfree(todel);
|
||||
/* Check if there are still any alarms set */
|
||||
p = alarmlist;
|
||||
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)
|
||||
some_alarms = 1;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
#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_irq(&alarm_lock);
|
||||
|
||||
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;
|
||||
|
||||
local_irq_save(flags);
|
||||
dir_shadow = *dir_oe[priv->minor];
|
||||
dir_shadow &= ~(arg & changeable_dir[priv->minor]);
|
||||
*dir_oe[priv->minor] = dir_shadow;
|
||||
local_irq_restore(flags);
|
||||
|
||||
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 */
|
||||
|
||||
inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long dir_shadow;
|
||||
|
||||
local_irq_save(flags);
|
||||
dir_shadow = *dir_oe[priv->minor];
|
||||
dir_shadow |= (arg & changeable_dir[priv->minor]);
|
||||
*dir_oe[priv->minor] = dir_shadow;
|
||||
local_irq_restore(flags);
|
||||
return dir_shadow;
|
||||
} /* setget_output */
|
||||
|
||||
static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
|
||||
|
||||
static int
|
||||
gpio_ioctl_unlocked(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 -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. */
|
||||
return *data_in[priv->minor];
|
||||
break;
|
||||
case IO_SETBITS:
|
||||
local_irq_save(flags);
|
||||
/* Set 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_CLRBITS:
|
||||
local_irq_save(flags);
|
||||
/* 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. */
|
||||
priv->highalarm |= arg;
|
||||
spin_lock_irqsave(&alarm_lock, flags);
|
||||
gpio_some_alarms = 1;
|
||||
if (priv->minor == GPIO_MINOR_A)
|
||||
gpio_pa_high_alarms |= arg;
|
||||
spin_unlock_irqrestore(&alarm_lock, flags);
|
||||
break;
|
||||
case IO_LOWALARM:
|
||||
/* Set alarm when bits with 1 in arg go low. */
|
||||
priv->lowalarm |= arg;
|
||||
spin_lock_irqsave(&alarm_lock, flags);
|
||||
gpio_some_alarms = 1;
|
||||
if (priv->minor == GPIO_MINOR_A)
|
||||
gpio_pa_low_alarms |= arg;
|
||||
spin_unlock_irqrestore(&alarm_lock, flags);
|
||||
break;
|
||||
case IO_CLRALARM:
|
||||
/* Clear alarm for bits with 1 in arg. */
|
||||
priv->highalarm &= ~arg;
|
||||
priv->lowalarm &= ~arg;
|
||||
spin_lock_irqsave(&alarm_lock, flags);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
if (gpio_pa_high_alarms & arg ||
|
||||
gpio_pa_low_alarms & arg)
|
||||
/* Must update the gpio_pa_*alarms masks */
|
||||
;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_lock, flags);
|
||||
break;
|
||||
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
|
||||
/* Read direction 0=input 1=output */
|
||||
return *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);
|
||||
break;
|
||||
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:
|
||||
{
|
||||
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 = *data_in[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 */
|
||||
val = *data_out[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.
|
||||
*/
|
||||
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;
|
||||
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:
|
||||
if (priv->minor == GPIO_MINOR_LEDS)
|
||||
return gpio_leds_ioctl(cmd, arg);
|
||||
else
|
||||
return -EINVAL;
|
||||
} /* switch */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
mutex_lock(&gpio_mutex);
|
||||
ret = gpio_ioctl_unlocked(file, cmd, arg);
|
||||
mutex_unlock(&gpio_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#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:
|
||||
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)
|
||||
{
|
||||
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 const struct file_operations gpio_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.poll = gpio_poll,
|
||||
.unlocked_ioctl = gpio_ioctl,
|
||||
.write = gpio_write,
|
||||
.open = gpio_open,
|
||||
.release = gpio_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
#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 */
|
||||
|
||||
static __init int
|
||||
gpio_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* 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);
|
||||
|
||||
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 */
|
||||
if (request_irq(TIMER0_INTR_VECT, gpio_poll_timer_interrupt,
|
||||
IRQF_SHARED, "gpio poll", &alarmlist))
|
||||
printk(KERN_ERR "timer0 irq for gpio\n");
|
||||
|
||||
if (request_irq(GIO_INTR_VECT, gpio_pa_interrupt,
|
||||
IRQF_SHARED, "gpio PA", &alarmlist))
|
||||
printk(KERN_ERR "PA irq for gpio\n");
|
||||
|
||||
#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);
|
174
arch/cris/arch-v32/drivers/mach-fs/nandflash.c
Normal file
174
arch/cris/arch-v32/drivers/mach-fs/nandflash.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* arch/cris/arch-v32/drivers/nandflash.c
|
||||
*
|
||||
* Copyright (c) 2004
|
||||
*
|
||||
* 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 <arch/memmap.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
|
||||
#define CLE_BIT 5
|
||||
#define ALE_BIT 6
|
||||
#define BY_BIT 7
|
||||
|
||||
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,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
unsigned long flags;
|
||||
reg_gio_rw_pa_dout dout;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din);
|
||||
return ((din.data & (1 << BY_BIT)) >> BY_BIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
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_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 */
|
||||
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 = ioremap(MEM_CSP0_START | MEM_NON_CACHEABLE, 8192);
|
||||
write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192);
|
||||
|
||||
if (!read_cs || !write_cs) {
|
||||
printk(KERN_ERR "CRISv32 NAND ioremap failed\n");
|
||||
err = -EIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
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);
|
||||
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);
|
||||
|
||||
/* 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->bbt_options = NAND_BBT_USE_FLASH; */
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(crisv32_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
return crisv32_mtd;
|
||||
|
||||
out_ior:
|
||||
iounmap((void *)read_cs);
|
||||
iounmap((void *)write_cs);
|
||||
out_mtd:
|
||||
kfree(wrapper);
|
||||
return NULL;
|
||||
}
|
||||
|
5
arch/cris/arch-v32/drivers/pci/Makefile
Normal file
5
arch/cris/arch-v32/drivers/pci/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for Etrax cardbus driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_CARDBUS) += bios.o dma.o
|
99
arch/cris/arch-v32/drivers/pci/bios.c
Normal file
99
arch/cris/arch-v32/drivers/pci/bios.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <arch/hwregs/intr_vect.h>
|
||||
|
||||
void pcibios_fixup_bus(struct pci_bus *b)
|
||||
{
|
||||
}
|
||||
|
||||
void pcibios_set_master(struct pci_dev *dev)
|
||||
{
|
||||
u8 lat;
|
||||
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
|
||||
printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
|
||||
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.
|
||||
*/
|
||||
prot = pgprot_val(vma->vm_page_prot);
|
||||
vma->vm_page_prot = __pgprot(prot);
|
||||
|
||||
/* Write-combine setting is ignored, it is changed via the mtrr
|
||||
* interfaces on this platform.
|
||||
*/
|
||||
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
resource_size_t
|
||||
pcibios_align_resource(void *data, const struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
resource_size_t start = res->start;
|
||||
|
||||
if ((res->flags & IORESOURCE_IO) && (start & 0x300))
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
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) {
|
||||
printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcibios_enable_irq(struct pci_dev *dev)
|
||||
{
|
||||
dev->irq = EXT_INTR_VECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = pcibios_enable_resources(dev, mask)) < 0)
|
||||
return err;
|
||||
|
||||
if (!dev->msi_enabled)
|
||||
pcibios_enable_irq(dev);
|
||||
return 0;
|
||||
}
|
50
arch/cris/arch-v32/drivers/pci/dma.c
Normal file
50
arch/cris/arch-v32/drivers/pci/dma.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Dynamic DMA mapping support.
|
||||
*
|
||||
* On cris there is no hardware dynamic DMA address translation,
|
||||
* so consistent alloc/free are merely page allocation/freeing.
|
||||
* The rest of the dynamic DMA mapping interface is implemented
|
||||
* in asm/pci.h.
|
||||
*
|
||||
* Borrowed from i386.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
void *dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp)
|
||||
{
|
||||
void *ret;
|
||||
int order = get_order(size);
|
||||
/* ignore region specifiers */
|
||||
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
|
||||
|
||||
if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
|
||||
return ret;
|
||||
|
||||
if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
|
||||
gfp |= GFP_DMA;
|
||||
|
||||
ret = (void *)__get_free_pages(gfp, order);
|
||||
|
||||
if (ret != NULL) {
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = virt_to_phys(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle)
|
||||
{
|
||||
int order = get_order(size);
|
||||
|
||||
if (!dma_release_from_coherent(dev, order, vaddr))
|
||||
free_pages((unsigned long)vaddr, order);
|
||||
}
|
||||
|
1557
arch/cris/arch-v32/drivers/sync_serial.c
Normal file
1557
arch/cris/arch-v32/drivers/sync_serial.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue