aboutsummaryrefslogtreecommitdiff
path: root/arch/cris/arch-v32/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'arch/cris/arch-v32/drivers')
-rw-r--r--arch/cris/arch-v32/drivers/Kconfig593
-rw-r--r--arch/cris/arch-v32/drivers/Makefile6
-rw-r--r--arch/cris/arch-v32/drivers/axisflashmap.c473
-rw-r--r--arch/cris/arch-v32/drivers/cryptocop.c152
-rw-r--r--arch/cris/arch-v32/drivers/gpio.c766
-rw-r--r--arch/cris/arch-v32/drivers/i2c.c236
-rw-r--r--arch/cris/arch-v32/drivers/i2c.h2
-rw-r--r--arch/cris/arch-v32/drivers/iop_fw_load.c33
-rw-r--r--arch/cris/arch-v32/drivers/mach-a3/Makefile6
-rw-r--r--arch/cris/arch-v32/drivers/mach-a3/gpio.c999
-rw-r--r--arch/cris/arch-v32/drivers/mach-a3/nandflash.c180
-rw-r--r--arch/cris/arch-v32/drivers/mach-fs/Makefile6
-rw-r--r--arch/cris/arch-v32/drivers/mach-fs/gpio.c979
-rw-r--r--arch/cris/arch-v32/drivers/mach-fs/nandflash.c (renamed from arch/cris/arch-v32/drivers/nandflash.c)124
-rw-r--r--arch/cris/arch-v32/drivers/pcf8563.c340
-rw-r--r--arch/cris/arch-v32/drivers/pci/bios.c56
-rw-r--r--arch/cris/arch-v32/drivers/pci/dma.c107
-rw-r--r--arch/cris/arch-v32/drivers/sync_serial.c1027
18 files changed, 3726 insertions, 2359 deletions
diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig
index a33097f9536..15a9ed1d579 100644
--- a/arch/cris/arch-v32/drivers/Kconfig
+++ b/arch/cris/arch-v32/drivers/Kconfig
@@ -1,65 +1,45 @@
+if ETRAX_ARCH_V32
+
config ETRAX_ETHERNET
bool "Ethernet support"
- depends on ETRAX_ARCH_V32
- select NET_ETHERNET
+ depends on ETRAX_ARCH_V32 && NETDEVICES
+ select MII
help
This option enables the ETRAX FS built-in 10/100Mbit Ethernet
controller.
-config ETRAX_ETHERNET_HW_CSUM
- bool "Hardware accelerated ethernet checksum and scatter/gather"
- depends on ETRAX_ETHERNET
- depends on ETRAX_STREAMCOPROC
- default y
- help
- Hardware acceleration of checksumming and scatter/gather
-
-config ETRAX_ETHERNET_IFACE0
+config ETRAX_NO_PHY
+ bool "PHY not present"
depends on ETRAX_ETHERNET
- bool "Enable network interface 0"
-
-config ETRAX_ETHERNET_IFACE1
- depends on ETRAX_ETHERNET
- bool "Enable network interface 1 (uses DMA6 and DMA7)"
-
-choice
- prompt "Network LED behavior"
- depends on ETRAX_ETHERNET
- default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
-
-config ETRAX_NETWORK_LED_ON_WHEN_LINK
- bool "LED_on_when_link"
- help
- Selecting LED_on_when_link will light the LED when there is a
- connection and will flash off when there is activity.
-
- Selecting LED_on_when_activity will light the LED only when
- there is activity.
-
- This setting will also affect the behaviour of other activity LEDs
- e.g. Bluetooth.
-
-config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
- bool "LED_on_when_activity"
help
- Selecting LED_on_when_link will light the LED when there is a
- connection and will flash off when there is activity.
-
- Selecting LED_on_when_activity will light the LED only when
- there is activity.
-
- This setting will also affect the behaviour of other activity LEDs
- e.g. Bluetooth.
-
-endchoice
+ 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
@@ -69,310 +49,24 @@ config ETRAX_SERIAL_PORT0
if you do not need DMA to something else.
ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
-choice
- prompt "Ser0 DMA in channel "
- depends on ETRAX_SERIAL_PORT0
- default ETRAX_SERIAL_PORT0_NO_DMA_IN
- help
- What DMA channel to use for ser0.
-
-
-config ETRAX_SERIAL_PORT0_NO_DMA_IN
- bool "Ser0 uses no DMA for input"
- help
- Do not use DMA for ser0 input.
-
-config ETRAX_SERIAL_PORT0_DMA7_IN
- bool "Ser0 uses DMA7 for input"
- depends on ETRAX_SERIAL_PORT0
- help
- Enables the DMA7 input channel for ser0 (ttyS0).
- If you do not enable DMA, an interrupt for each character will be
- used when receiveing data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-choice
- prompt "Ser0 DMA out channel"
- depends on ETRAX_SERIAL_PORT0
- default ETRAX_SERIAL_PORT0_NO_DMA_OUT
-
-config ETRAX_SERIAL_PORT0_NO_DMA_OUT
- bool "Ser0 uses no DMA for output"
- help
- Do not use DMA for ser0 output.
-
-config ETRAX_SERIAL_PORT0_DMA6_OUT
- bool "Ser0 uses DMA6 for output"
- depends on ETRAX_SERIAL_PORT0
- help
- Enables the DMA6 output channel for ser0 (ttyS0).
- If you do not enable DMA, an interrupt for each character will be
- used when transmitting data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-config ETRAX_SER0_DTR_BIT
- string "Ser 0 DTR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT0
-
-config ETRAX_SER0_RI_BIT
- string "Ser 0 RI bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT0
-
-config ETRAX_SER0_DSR_BIT
- string "Ser 0 DSR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT0
-
-config ETRAX_SER0_CD_BIT
- string "Ser 0 CD bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT0
-
config ETRAX_SERIAL_PORT1
bool "Serial port 1 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser1 (ttyS1).
-choice
- prompt "Ser1 DMA in channel "
- depends on ETRAX_SERIAL_PORT1
- default ETRAX_SERIAL_PORT1_NO_DMA_IN
- help
- What DMA channel to use for ser1.
-
-
-config ETRAX_SERIAL_PORT1_NO_DMA_IN
- bool "Ser1 uses no DMA for input"
- help
- Do not use DMA for ser1 input.
-
-config ETRAX_SERIAL_PORT1_DMA5_IN
- bool "Ser1 uses DMA5 for input"
- depends on ETRAX_SERIAL_PORT1
- help
- Enables the DMA5 input channel for ser1 (ttyS1).
- If you do not enable DMA, an interrupt for each character will be
- used when receiveing data.
- Normally you want this on, unless you use the DMA channel for
- something else.
-
-endchoice
-
-choice
- prompt "Ser1 DMA out channel "
- depends on ETRAX_SERIAL_PORT1
- default ETRAX_SERIAL_PORT1_NO_DMA_OUT
- help
- What DMA channel to use for ser1.
-
-config ETRAX_SERIAL_PORT1_NO_DMA_OUT
- bool "Ser1 uses no DMA for output"
- help
- Do not use DMA for ser1 output.
-
-config ETRAX_SERIAL_PORT1_DMA4_OUT
- bool "Ser1 uses DMA4 for output"
- depends on ETRAX_SERIAL_PORT1
- help
- Enables the DMA4 output channel for ser1 (ttyS1).
- If you do not enable DMA, an interrupt for each character will be
- used when transmitting data.
- Normally you want this on, unless you use the DMA channel for
- something else.
-
-endchoice
-
-config ETRAX_SER1_DTR_BIT
- string "Ser 1 DTR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT1
-
-config ETRAX_SER1_RI_BIT
- string "Ser 1 RI bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT1
-
-config ETRAX_SER1_DSR_BIT
- string "Ser 1 DSR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT1
-
-config ETRAX_SER1_CD_BIT
- string "Ser 1 CD bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT1
-
config ETRAX_SERIAL_PORT2
bool "Serial port 2 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser2 (ttyS2).
-choice
- prompt "Ser2 DMA in channel "
- depends on ETRAX_SERIAL_PORT2
- default ETRAX_SERIAL_PORT2_NO_DMA_IN
- help
- What DMA channel to use for ser2.
-
-
-config ETRAX_SERIAL_PORT2_NO_DMA_IN
- bool "Ser2 uses no DMA for input"
- help
- Do not use DMA for ser2 input.
-
-config ETRAX_SERIAL_PORT2_DMA3_IN
- bool "Ser2 uses DMA3 for input"
- depends on ETRAX_SERIAL_PORT2
- help
- Enables the DMA3 input channel for ser2 (ttyS2).
- If you do not enable DMA, an interrupt for each character will be
- used when receiveing data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-choice
- prompt "Ser2 DMA out channel"
- depends on ETRAX_SERIAL_PORT2
- default ETRAX_SERIAL_PORT2_NO_DMA_OUT
-
-config ETRAX_SERIAL_PORT2_NO_DMA_OUT
- bool "Ser2 uses no DMA for output"
- help
- Do not use DMA for ser2 output.
-
-config ETRAX_SERIAL_PORT2_DMA2_OUT
- bool "Ser2 uses DMA2 for output"
- depends on ETRAX_SERIAL_PORT2
- help
- Enables the DMA2 output channel for ser2 (ttyS2).
- If you do not enable DMA, an interrupt for each character will be
- used when transmitting data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-config ETRAX_SER2_DTR_BIT
- string "Ser 2 DTR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT2
-
-config ETRAX_SER2_RI_BIT
- string "Ser 2 RI bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT2
-
-config ETRAX_SER2_DSR_BIT
- string "Ser 2 DSR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT2
-
-config ETRAX_SER2_CD_BIT
- string "Ser 2 CD bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT2
-
config ETRAX_SERIAL_PORT3
bool "Serial port 3 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser3 (ttyS3).
-choice
- prompt "Ser3 DMA in channel "
- depends on ETRAX_SERIAL_PORT3
- default ETRAX_SERIAL_PORT3_NO_DMA_IN
- help
- What DMA channel to use for ser3.
-
-
-config ETRAX_SERIAL_PORT3_NO_DMA_IN
- bool "Ser3 uses no DMA for input"
- help
- Do not use DMA for ser3 input.
-
-config ETRAX_SERIAL_PORT3_DMA9_IN
- bool "Ser3 uses DMA9 for input"
- depends on ETRAX_SERIAL_PORT3
- help
- Enables the DMA9 input channel for ser3 (ttyS3).
- If you do not enable DMA, an interrupt for each character will be
- used when receiveing data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-choice
- prompt "Ser3 DMA out channel"
- depends on ETRAX_SERIAL_PORT3
- default ETRAX_SERIAL_PORT3_NO_DMA_OUT
-
-config ETRAX_SERIAL_PORT3_NO_DMA_OUT
- bool "Ser3 uses no DMA for output"
- help
- Do not use DMA for ser3 output.
-
-config ETRAX_SERIAL_PORT3_DMA8_OUT
- bool "Ser3 uses DMA8 for output"
- depends on ETRAX_SERIAL_PORT3
- help
- Enables the DMA8 output channel for ser3 (ttyS3).
- If you do not enable DMA, an interrupt for each character will be
- used when transmitting data.
- Normally you want to use DMA, unless you use the DMA channel for
- something else.
-
-endchoice
-
-config ETRAX_SER3_DTR_BIT
- string "Ser 3 DTR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT3
-
-config ETRAX_SER3_RI_BIT
- string "Ser 3 RI bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT3
-
-config ETRAX_SER3_DSR_BIT
- string "Ser 3 DSR bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT3
-
-config ETRAX_SER3_CD_BIT
- string "Ser 3 CD bit (empty = not used)"
- depends on ETRAX_SERIAL_PORT3
-
-config ETRAX_RS485
- bool "RS-485 support"
- depends on ETRAX_SERIAL
- help
- Enables support for RS-485 serial communication. For a primer on
- RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
-
-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.
- Axis 2400/2401 must disable receiver.
-
-config ETRAX_AXISFLASHMAP
- bool "Axis flash-map support"
- depends on ETRAX_ARCH_V32
- select MTD
- select MTD_CFI
- select MTD_CFI_AMDSTD
- select MTD_OBSOLETE_CHIPS
- select MTD_AMDSTD
- select MTD_CHAR
- select MTD_BLOCK
- select MTD_PARTITIONS
- select MTD_CONCAT
- select MTD_COMPLEX_MAPPINGS
- help
- This option enables MTD mapping of flash devices. Needed to use
- flash memories. If unsure, say Y.
-
config ETRAX_SYNCHRONOUS_SERIAL
bool "Synchronous serial-port support"
depends on ETRAX_ARCH_V32
@@ -394,7 +88,7 @@ config ETRAX_SYNCHRONOUS_SERIAL0_DMA
config ETRAX_SYNCHRONOUS_SERIAL_PORT1
bool "Synchronous serial port 1 enabled"
- depends on ETRAX_SYNCHRONOUS_SERIAL
+ depends on ETRAX_SYNCHRONOUS_SERIAL && ETRAXFS
help
Enabled synchronous serial port 1.
@@ -405,6 +99,27 @@ config ETRAX_SYNCHRONOUS_SERIAL1_DMA
A synchronous serial port can run in manual or DMA mode.
Selecting this option will make it run in DMA mode.
+config ETRAX_AXISFLASHMAP
+ bool "Axis flash-map support"
+ depends on ETRAX_ARCH_V32
+ select MTD
+ select MTD_CFI
+ select MTD_CFI_AMDSTD
+ select MTD_JEDECPROBE
+ select MTD_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
@@ -425,42 +140,32 @@ config ETRAX_NANDFLASH
This option enables MTD mapping of NAND flash devices. Needed to use
NAND flash memories. If unsure, say Y.
+config ETRAX_NANDBOOT
+ bool "Boot from NAND flash"
+ depends on ETRAX_NANDFLASH
+ help
+ This options enables booting from NAND flash devices.
+ Say Y if your boot code, kernel and root file system is in
+ NAND flash. Say N if they are in NOR flash.
+
config ETRAX_I2C
bool "I2C driver"
depends on ETRAX_ARCH_V32
help
- This option enabled the I2C driver used by e.g. the RTC driver.
+ This option enables the I2C driver used by e.g. the RTC driver.
-config ETRAX_I2C_DATA_PORT
+config ETRAX_V32_I2C_DATA_PORT
string "I2C data pin"
depends on ETRAX_I2C
help
The pin to use for I2C data.
-config ETRAX_I2C_CLK_PORT
+config ETRAX_V32_I2C_CLK_PORT
string "I2C clock pin"
depends on ETRAX_I2C
help
The pin to use for I2C clock.
-config ETRAX_RTC
- bool "Real Time Clock support"
- depends on ETRAX_ARCH_V32
- help
- Enabled RTC support.
-
-choice
- prompt "RTC chip"
- depends on ETRAX_RTC
- default ETRAX_PCF8563
-
-config ETRAX_PCF8563
- bool "PCF8563"
- help
- Philips PCF8563 RTC
-
-endchoice
-
config ETRAX_GPIO
bool "GPIO support"
depends on ETRAX_ARCH_V32
@@ -476,33 +181,36 @@ config ETRAX_GPIO
Remember that you need to setup the port directions appropriately in
the General configuration.
-config ETRAX_PA_BUTTON_BITMASK
- hex "PA-buttons bitmask"
+config ETRAX_VIRTUAL_GPIO
+ bool "Virtual GPIO support"
depends on ETRAX_GPIO
- default "0x02"
help
- This is a bitmask (8 bits) with information about what bits on PA
- that are used for buttons.
- Most products has a so called TEST button on PA1, if that is true
- use 0x02 here.
- Use 00 if there are no buttons on PA.
- If the bitmask is <> 00 a button driver will be included in the gpio
- driver. ETRAX general I/O support must be enabled.
+ Enables the virtual Etrax general port device (major 120, minor 6).
+ It uses an I/O expander for the I2C-bus.
+
+config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN
+ int "Virtual GPIO interrupt pin on PA pin"
+ range 0 7
+ depends on ETRAX_VIRTUAL_GPIO
+ help
+ The pin to use on PA for virtual gpio interrupt.
config ETRAX_PA_CHANGEABLE_DIR
hex "PA user changeable dir mask"
depends on ETRAX_GPIO
- default "0x00"
+ default "0x00" if ETRAXFS
+ default "0x00000000" if !ETRAXFS
help
This is a bitmask (8 bits) with information of what bits in PA that a
user can change direction on using ioctl's.
Bit set = changeable.
- You probably want 0x00 here, but it depends on your hardware.
+ You probably want 0 here, but it depends on your hardware.
config ETRAX_PA_CHANGEABLE_BITS
hex "PA user changeable bits mask"
depends on ETRAX_GPIO
- default "0x00"
+ default "0x00" if ETRAXFS
+ default "0x00000000" if !ETRAXFS
help
This is a bitmask (8 bits) with information of what bits in PA
that a user can change the value on using ioctl's.
@@ -511,17 +219,19 @@ config ETRAX_PA_CHANGEABLE_BITS
config ETRAX_PB_CHANGEABLE_DIR
hex "PB user changeable dir mask"
depends on ETRAX_GPIO
- default "0x00000"
+ default "0x00000" if ETRAXFS
+ default "0x00000000" if !ETRAXFS
help
This is a bitmask (18 bits) with information of what bits in PB
that a user can change direction on using ioctl's.
Bit set = changeable.
- You probably want 0x00000 here, but it depends on your hardware.
+ You probably want 0 here, but it depends on your hardware.
config ETRAX_PB_CHANGEABLE_BITS
hex "PB user changeable bits mask"
depends on ETRAX_GPIO
- default "0x00000"
+ default "0x00000" if ETRAXFS
+ default "0x00000000" if !ETRAXFS
help
This is a bitmask (18 bits) with information of what bits in PB
that a user can change the value on using ioctl's.
@@ -530,17 +240,19 @@ config ETRAX_PB_CHANGEABLE_BITS
config ETRAX_PC_CHANGEABLE_DIR
hex "PC user changeable dir mask"
depends on ETRAX_GPIO
- default "0x00000"
+ default "0x00000" if ETRAXFS
+ default "0x00000000" if !ETRAXFS
help
This is a bitmask (18 bits) with information of what bits in PC
that a user can change direction on using ioctl's.
Bit set = changeable.
- You probably want 0x00000 here, but it depends on your hardware.
+ You probably want 0 here, but it depends on your hardware.
config ETRAX_PC_CHANGEABLE_BITS
hex "PC user changeable bits mask"
depends on ETRAX_GPIO
- default "0x00000"
+ default "0x00000" if ETRAXFS
+ default "0x00000000" if ETRAXFS
help
This is a bitmask (18 bits) with information of what bits in PC
that a user can change the value on using ioctl's.
@@ -548,7 +260,7 @@ config ETRAX_PC_CHANGEABLE_BITS
config ETRAX_PD_CHANGEABLE_DIR
hex "PD user changeable dir mask"
- depends on ETRAX_GPIO
+ depends on ETRAX_GPIO && ETRAXFS
default "0x00000"
help
This is a bitmask (18 bits) with information of what bits in PD
@@ -558,7 +270,7 @@ config ETRAX_PD_CHANGEABLE_DIR
config ETRAX_PD_CHANGEABLE_BITS
hex "PD user changeable bits mask"
- depends on ETRAX_GPIO
+ depends on ETRAX_GPIO && ETRAXFS
default "0x00000"
help
This is a bitmask (18 bits) with information of what bits in PD
@@ -567,7 +279,7 @@ config ETRAX_PD_CHANGEABLE_BITS
config ETRAX_PE_CHANGEABLE_DIR
hex "PE user changeable dir mask"
- depends on ETRAX_GPIO
+ depends on ETRAX_GPIO && ETRAXFS
default "0x00000"
help
This is a bitmask (18 bits) with information of what bits in PE
@@ -577,38 +289,43 @@ config ETRAX_PE_CHANGEABLE_DIR
config ETRAX_PE_CHANGEABLE_BITS
hex "PE user changeable bits mask"
- depends on ETRAX_GPIO
+ depends on ETRAX_GPIO && ETRAXFS
default "0x00000"
help
This is a bitmask (18 bits) with information of what bits in PE
that a user can change the value on using ioctl's.
Bit set = changeable.
-config ETRAX_IDE
- bool "ATA/IDE support"
- depends on ETRAX_ARCH_V32
- select IDE
- select BLK_DEV_IDE
- select BLK_DEV_IDEDISK
- select BLK_DEV_IDECD
- select BLK_DEV_IDEDMA
+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
- Enables the ETRAX IDE driver.
+ This is a bitmask (16 bits) with information of what bits in PV
+ that a user can change the value on using ioctl's.
+ Bit set = changeable.
config ETRAX_CARDBUS
bool "Cardbus support"
depends on ETRAX_ARCH_V32
- select PCCARD
- select CARDBUS
- select HOTPLUG
- select PCCARD_NONSTATIC
help
- Enabled the ETRAX Carbus driver.
+ 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"
@@ -623,3 +340,97 @@ config ETRAX_STREAMCOPROC
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
diff --git a/arch/cris/arch-v32/drivers/Makefile b/arch/cris/arch-v32/drivers/Makefile
index a359cd20ae7..39aa3c117a8 100644
--- a/arch/cris/arch-v32/drivers/Makefile
+++ b/arch/cris/arch-v32/drivers/Makefile
@@ -4,10 +4,10 @@
obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
-obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
-obj-$(CONFIG_ETRAX_GPIO) += gpio.o
+obj-$(CONFIG_ETRAXFS) += mach-fs/
+obj-$(CONFIG_CRIS_MACH_ARTPEC3) += mach-a3/
obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o
-obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o
obj-$(CONFIG_ETRAX_I2C) += i2c.o
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
obj-$(CONFIG_PCI) += pci/
+obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o
diff --git a/arch/cris/arch-v32/drivers/axisflashmap.c b/arch/cris/arch-v32/drivers/axisflashmap.c
index b679f983b90..28dd77144e8 100644
--- a/arch/cris/arch-v32/drivers/axisflashmap.c
+++ b/arch/cris/arch-v32/drivers/axisflashmap.c
@@ -1,7 +1,7 @@
/*
* Physical mapping layer for MTD using the Axis partitiontable format
*
- * Copyright (c) 2001, 2002, 2003 Axis Communications AB
+ * Copyright (c) 2001-2007 Axis Communications AB
*
* This file is under the GPL.
*
@@ -10,15 +10,11 @@
* tells us what other partitions to define. If there isn't, we use a default
* partition split defined below.
*
- * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5
- * with minor changes.
- *
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -28,7 +24,6 @@
#include <linux/mtd/mtdram.h>
#include <linux/mtd/partitions.h>
-#include <asm/arch/hwregs/config_defs.h>
#include <asm/axisflashmap.h>
#include <asm/mmu.h>
@@ -38,16 +33,24 @@
#define FLASH_UNCACHED_ADDR KSEG_E
#define FLASH_CACHED_ADDR KSEG_F
+#define PAGESIZE (512)
+
#if CONFIG_ETRAX_FLASH_BUSWIDTH==1
#define flash_data __u8
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
#define flash_data __u16
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
-#define flash_data __u16
+#define flash_data __u32
#endif
/* From head.S */
-extern unsigned long romfs_start, romfs_length, romfs_in_flash;
+extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */
+extern unsigned long romfs_start, romfs_length;
+extern unsigned long nand_boot; /* 1 when booted from nand flash */
+
+struct partition_name {
+ char name[6];
+};
/* The master mtd for the entire flash. */
struct mtd_info* axisflash_mtd = NULL;
@@ -113,32 +116,20 @@ static struct map_info map_cse1 = {
.map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
};
-/* If no partition-table was found, we use this default-set. */
-#define MAX_PARTITIONS 7
-#define NUM_DEFAULT_PARTITIONS 3
+#define MAX_PARTITIONS 7
+#ifdef CONFIG_ETRAX_NANDBOOT
+#define NUM_DEFAULT_PARTITIONS 4
+#define DEFAULT_ROOTFS_PARTITION_NO 2
+#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */
+#else
+#define NUM_DEFAULT_PARTITIONS 3
+#define DEFAULT_ROOTFS_PARTITION_NO (-1)
+#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */
+#endif
-/*
- * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the
- * size of one flash block and "filesystem"-partition needs 5 blocks to be able
- * to use JFFS.
- */
-static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
- {
- .name = "boot firmware",
- .size = CONFIG_ETRAX_PTABLE_SECTOR,
- .offset = 0
- },
- {
- .name = "kernel",
- .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR),
- .offset = CONFIG_ETRAX_PTABLE_SECTOR
- },
- {
- .name = "filesystem",
- .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR,
- .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR)
- }
-};
+#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS)
+#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS
+#endif
/* Initialize the ones normally used. */
static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
@@ -179,6 +170,56 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
},
};
+
+/* If no partition-table was found, we use this default-set.
+ * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most
+ * likely the size of one flash block and "filesystem"-partition needs
+ * to be >=5 blocks to be able to use JFFS.
+ */
+static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
+ {
+ .name = "boot firmware",
+ .size = CONFIG_ETRAX_PTABLE_SECTOR,
+ .offset = 0
+ },
+ {
+ .name = "kernel",
+ .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
+ .offset = CONFIG_ETRAX_PTABLE_SECTOR
+ },
+#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR)
+#ifdef CONFIG_ETRAX_NANDBOOT
+ {
+ .name = "rootfs",
+ .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
+ .offset = FILESYSTEM_SECTOR
+ },
+#undef FILESYSTEM_SECTOR
+#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR)
+#endif
+ {
+ .name = "rwfs",
+ .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR,
+ .offset = FILESYSTEM_SECTOR
+ }
+};
+
+#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
+/* Main flash device */
+static struct mtd_partition main_partition = {
+ .name = "main",
+ .size = 0,
+ .offset = 0
+};
+#endif
+
+/* 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).
@@ -191,13 +232,12 @@ static struct mtd_info *probe_cs(struct map_info *map_cs)
"%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_AMDSTD
- mtd_cs = do_map_probe("amd_flash", map_cs);
-#endif
#ifdef CONFIG_MTD_CFI
- if (!mtd_cs) {
- mtd_cs = do_map_probe("cfi_probe", map_cs);
- }
+ mtd_cs = do_map_probe("cfi_probe", map_cs);
+#endif
+#ifdef CONFIG_MTD_JEDECPROBE
+ if (!mtd_cs)
+ mtd_cs = do_map_probe("jedec_probe", map_cs);
#endif
return mtd_cs;
@@ -218,9 +258,8 @@ static struct mtd_info *flash_probe(void)
{
struct mtd_info *mtd_cse0;
struct mtd_info *mtd_cse1;
- struct mtd_info *mtd_nand = NULL;
struct mtd_info *mtd_total;
- struct mtd_info *mtds[3];
+ struct mtd_info *mtds[2];
int count = 0;
if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL)
@@ -228,18 +267,12 @@ static struct mtd_info *flash_probe(void)
if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL)
mtds[count++] = mtd_cse1;
-#ifdef CONFIG_ETRAX_NANDFLASH
- if ((mtd_nand = crisv32_nand_flash_probe()) != NULL)
- mtds[count++] = mtd_nand;
-#endif
-
- if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) {
+ if (!mtd_cse0 && !mtd_cse1) {
/* No chip found. */
return NULL;
}
if (count > 1) {
-#ifdef CONFIG_MTD_CONCAT
/* 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
@@ -247,71 +280,163 @@ static struct mtd_info *flash_probe(void)
* So we use the MTD concatenation layer instead of further
* complicating the probing procedure.
*/
- mtd_total = mtd_concat_create(mtds,
- count,
- "cse0+cse1+nand");
-#else
- printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel "
- "(mis)configuration!\n", map_cse0.name, map_cse1.name);
- mtd_toal = NULL;
-#endif
+ 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);
+ map_cse0.name, map_cse1.name);
/* The best we can do now is to only use what we found
- * at cse0.
- */
+ * at cse0. */
mtd_total = mtd_cse0;
map_destroy(mtd_cse1);
}
- } else {
- mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand;
-
- }
+ } else
+ mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1;
return mtd_total;
}
-extern unsigned long crisv32_nand_boot;
-extern unsigned long crisv32_nand_cramfs_offset;
-
/*
* Probe the flash chip(s) and, if it succeeds, read the partition-table
* and register the partitions with MTD.
*/
static int __init init_axis_flash(void)
{
- struct mtd_info *mymtd;
+ struct mtd_info *main_mtd;
+ struct mtd_info *aux_mtd = NULL;
int err = 0;
int pidx = 0;
struct partitiontable_head *ptable_head = NULL;
struct partitiontable_entry *ptable;
- int use_default_ptable = 1; /* Until proven otherwise. */
- const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n";
- static char page[512];
+ int ptable_ok = 0;
+ static char page[PAGESIZE];
size_t len;
+ int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */
+ int part;
+
+ /* We need a root fs. If it resides in RAM, we need to use an
+ * MTDRAM device, so it must be enabled in the kernel config,
+ * but its size must be configured as 0 so as not to conflict
+ * with our usage.
+ */
+#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
+ if (!romfs_in_flash && !nand_boot) {
+ printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
+ "device; configure CONFIG_MTD_MTDRAM with size = 0!\n");
+ panic("This kernel cannot boot from RAM!\n");
+ }
+#endif
+
+ 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);
-#ifndef CONFIG_ETRAXFS_SIM
- mymtd = flash_probe();
- mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page);
- ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET);
+#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 (!mymtd) {
+ if (!main_mtd && !aux_mtd) {
/* There's no reason to use this module if no flash chip can
* be identified. Make sure that's understood.
*/
printk(KERN_INFO "axisflashmap: Found no flash chip.\n");
- } else {
- printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
- mymtd->name, mymtd->size);
- axisflash_mtd = mymtd;
}
- if (mymtd) {
- mymtd->owner = THIS_MODULE;
+#if 0 /* Dump flash memory so we can see what is going on */
+ if (main_mtd) {
+ int sectoraddr, i;
+ for (sectoraddr = 0; sectoraddr < 2*65536+4096;
+ sectoraddr += PAGESIZE) {
+ main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len,
+ page);
+ printk(KERN_INFO
+ "Sector at %d (length %d):\n",
+ sectoraddr, len);
+ for (i = 0; i < PAGESIZE; i += 16) {
+ printk(KERN_INFO
+ "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n",
+ page[i] & 255, page[i+1] & 255,
+ page[i+2] & 255, page[i+3] & 255,
+ page[i+4] & 255, page[i+5] & 255,
+ page[i+6] & 255, page[i+7] & 255,
+ page[i+8] & 255, page[i+9] & 255,
+ page[i+10] & 255, page[i+11] & 255,
+ page[i+12] & 255, page[i+13] & 255,
+ page[i+14] & 255, page[i+15] & 255);
+ }
+ }
+ }
+#endif
+
+ if (main_mtd) {
+ main_mtd->owner = THIS_MODULE;
+ axisflash_mtd = main_mtd;
+
+ loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR;
+
+ /* First partition (rescue) is always set to the default. */
+ pidx++;
+#ifdef CONFIG_ETRAX_NANDBOOT
+ /* We know where the partition table should be located,
+ * it will be in first good block after that.
+ */
+ int blockstat;
+ do {
+ blockstat = 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
}
- pidx++; /* First partition is always set to the default. */
if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
&& (ptable_head->size <
@@ -324,7 +449,6 @@ static int __init init_axis_flash(void)
/* Looks like a start, sane length and end of a
* partition table, lets check csum etc.
*/
- int ptable_ok = 0;
struct partitiontable_entry *max_addr =
(struct partitiontable_entry *)
((unsigned long)ptable_head + sizeof(*ptable_head) +
@@ -348,103 +472,142 @@ static int __init init_axis_flash(void)
ptable_ok = (csum == ptable_head->checksum);
/* Read the entries and use/show the info. */
- printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
+ printk(KERN_INFO "axisflashmap: "
+ "Found a%s partition table at 0x%p-0x%p.\n",
(ptable_ok ? " valid" : "n invalid"), ptable_head,
max_addr);
/* We have found a working bootblock. Now read the
- * partition table. Scan the table. It ends when
- * there is 0xffffffff, that is, empty flash.
+ * partition table. Scan the table. It ends with 0xffffffff.
*/
while (ptable_ok
- && ptable->offset != 0xffffffff
+ && ptable->offset != PARTITIONTABLE_END_MARKER
&& ptable < max_addr
- && pidx < MAX_PARTITIONS) {
+ && pidx < MAX_PARTITIONS - 1) {
- axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0);
- axis_partitions[pidx].size = ptable->size;
-
- printk(pmsg, pidx, axis_partitions[pidx].offset,
- axis_partitions[pidx].size);
+ axis_partitions[pidx].offset = offset + ptable->offset;
+#ifdef CONFIG_ETRAX_NANDFLASH
+ if (main_mtd->type == MTD_NANDFLASH) {
+ axis_partitions[pidx].size =
+ (((ptable+1)->offset ==
+ PARTITIONTABLE_END_MARKER) ?
+ main_mtd->size :
+ ((ptable+1)->offset + offset)) -
+ (ptable->offset + offset);
+
+ } else
+#endif /* CONFIG_ETRAX_NANDFLASH */
+ axis_partitions[pidx].size = ptable->size;
+#ifdef CONFIG_ETRAX_NANDBOOT
+ /* Save partition number of jffs2 ro partition.
+ * Needed if RAM booting or root file system in RAM.
+ */
+ if (!nand_boot &&
+ ram_rootfs_partition < 0 && /* not already set */
+ ptable->type == PARTITION_TYPE_JFFS2 &&
+ (ptable->flags & PARTITION_FLAGS_READONLY_MASK) ==
+ PARTITION_FLAGS_READONLY)
+ ram_rootfs_partition = pidx;
+#endif /* CONFIG_ETRAX_NANDBOOT */
pidx++;
ptable++;
}
- use_default_ptable = !ptable_ok;
}
- if (romfs_in_flash) {
- /* Add an overlapping device for the root partition (romfs). */
+ /* Decide whether to use default partition table. */
+ /* Only use default table if we actually have a device (main_mtd) */
- axis_partitions[pidx].name = "romfs";
- if (crisv32_nand_boot) {
- char* data = kmalloc(1024, GFP_KERNEL);
- int len;
- int offset = crisv32_nand_cramfs_offset & ~(1024-1);
- char* tmp;
-
- mymtd->read(mymtd, offset, 1024, &len, data);
- tmp = &data[crisv32_nand_cramfs_offset % 512];
- axis_partitions[pidx].size = *(unsigned*)(tmp + 4);
- axis_partitions[pidx].offset = crisv32_nand_cramfs_offset;
- kfree(data);
- } else {
- axis_partitions[pidx].size = romfs_length;
- axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
- }
+ struct mtd_partition *partition = &axis_partitions[0];
+ if (main_mtd && !ptable_ok) {
+ memcpy(axis_partitions, axis_default_partitions,
+ sizeof(axis_default_partitions));
+ pidx = NUM_DEFAULT_PARTITIONS;
+ ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO;
+ }
+ /* Add artificial partitions for rootfs if necessary */
+ if (romfs_in_flash) {
+ /* rootfs is in directly accessible flash memory = NOR flash.
+ Add an overlapping device for the rootfs partition. */
+ printk(KERN_INFO "axisflashmap: Adding partition for "
+ "overlapping root file system image\n");
+ axis_partitions[pidx].size = romfs_length;
+ axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
+ axis_partitions[pidx].name = "romfs";
axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
-
- printk(KERN_INFO
- " Adding readonly flash partition for romfs image:\n");
- printk(pmsg, pidx, axis_partitions[pidx].offset,
- axis_partitions[pidx].size);
+ ram_rootfs_partition = -1;
pidx++;
- }
-
- if (mymtd) {
- if (use_default_ptable) {
- printk(KERN_INFO " Using default partition table.\n");
- err = add_mtd_partitions(mymtd, axis_default_partitions,
- NUM_DEFAULT_PARTITIONS);
- } else {
- err = add_mtd_partitions(mymtd, axis_partitions, pidx);
+ } else if (romfs_length && !nand_boot) {
+ /* romfs exists in memory, but not in flash, so must be in RAM.
+ * Configure an MTDRAM partition. */
+ if (ram_rootfs_partition < 0) {
+ /* None set yet, put it at the end */
+ ram_rootfs_partition = pidx;
+ pidx++;
}
+ printk(KERN_INFO "axisflashmap: Adding partition for "
+ "root file system image in RAM\n");
+ axis_partitions[ram_rootfs_partition].size = romfs_length;
+ axis_partitions[ram_rootfs_partition].offset = romfs_start;
+ axis_partitions[ram_rootfs_partition].name = "romfs";
+ axis_partitions[ram_rootfs_partition].mask_flags |=
+ MTD_WRITEABLE;
+ }
- if (err) {
- panic("axisflashmap could not add MTD partitions!\n");
- }
+#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
+ if (main_mtd) {
+ main_partition.size = main_mtd->size;
+ err = mtd_device_register(main_mtd, &main_partition, 1);
+ if (err)
+ panic("axisflashmap: Could not initialize "
+ "partition for whole main mtd device!\n");
}
-/* CONFIG_EXTRAXFS_SIM */
#endif
- if (!romfs_in_flash) {
- /* Create an RAM device for the root partition (romfs). */
-
-#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
- /* No use trying to boot this kernel from RAM. Panic! */
- printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
- "device due to kernel (mis)configuration!\n");
- panic("This kernel cannot boot from RAM!\n");
-#else
- struct mtd_info *mtd_ram;
-
- mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info),
- GFP_KERNEL);
- if (!mtd_ram) {
- panic("axisflashmap couldn't allocate memory for "
- "mtd_info!\n");
+ /* 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);
}
+ }
- printk(KERN_INFO " Adding RAM partition for romfs image:\n");
- printk(pmsg, pidx, romfs_start, romfs_length);
+ 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");
- err = mtdram_init_device(mtd_ram, (void*)romfs_start,
- romfs_length, "romfs");
- if (err) {
- panic("axisflashmap could not initialize MTD RAM "
- "device!\n");
- }
-#endif
}
return err;
diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c
index 501fa52d8d3..877da190823 100644
--- a/arch/cris/arch-v32/drivers/cryptocop.c
+++ b/arch/cris/arch-v32/drivers/cryptocop.c
@@ -1,8 +1,7 @@
-/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $
- *
+/*
* Stream co-processor driver for the ETRAX FS
*
- * Copyright (C) 2003-2005 Axis Communications AB
+ * Copyright (C) 2003-2007 Axis Communications AB
*/
#include <linux/init.h>
@@ -17,7 +16,7 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -25,17 +24,29 @@
#include <asm/signal.h>
#include <asm/irq.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/hwregs/dma.h>
-#include <asm/arch/hwregs/reg_map.h>
-#include <asm/arch/hwregs/reg_rdwr.h>
-#include <asm/arch/hwregs/intr_vect_defs.h>
-
-#include <asm/arch/hwregs/strcop.h>
-#include <asm/arch/hwregs/strcop_defs.h>
-#include <asm/arch/cryptocop.h>
-
-
+#include <dma.h>
+#include <hwregs/dma.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/intr_vect_defs.h>
+
+#include <hwregs/strcop.h>
+#include <hwregs/strcop_defs.h>
+#include <cryptocop.h>
+
+#ifdef CONFIG_ETRAXFS
+#define IN_DMA 9
+#define OUT_DMA 8
+#define IN_DMA_INST regi_dma9
+#define OUT_DMA_INST regi_dma8
+#define DMA_IRQ DMA9_INTR_VECT
+#else
+#define IN_DMA 3
+#define OUT_DMA 2
+#define IN_DMA_INST regi_dma3
+#define OUT_DMA_INST regi_dma2
+#define DMA_IRQ DMA3_INTR_VECT
+#endif
#define DESCR_ALLOC_PAD (31)
@@ -205,7 +216,7 @@ static int cryptocop_open(struct inode *, struct file *);
static int cryptocop_release(struct inode *, struct file *);
-static int cryptocop_ioctl(struct inode *inode, struct file *file,
+static long cryptocop_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
static void cryptocop_start_job(void);
@@ -266,11 +277,12 @@ static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op);
-struct file_operations cryptocop_fops = {
- owner: THIS_MODULE,
- open: cryptocop_open,
- release: cryptocop_release,
- ioctl: cryptocop_ioctl
+const struct file_operations cryptocop_fops = {
+ .owner = THIS_MODULE,
+ .open = cryptocop_open,
+ .release = cryptocop_release,
+ .unlocked_ioctl = cryptocop_ioctl,
+ .llseek = noop_llseek,
};
@@ -616,9 +628,9 @@ static int create_output_descriptors(struct cryptocop_operation *operation, int
cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset);
cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength;
+ assert(desc_len >= dlength);
desc_len -= dlength;
*iniov_offset += dlength;
- assert(desc_len >= 0);
if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) {
*iniov_offset = 0;
++(*iniov_ix);
@@ -1382,11 +1394,10 @@ static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char
if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH;
- p = kmalloc(padlen, alloc_flag);
- if (!pad) return -ENOMEM;
+ p = kzalloc(padlen, alloc_flag);
+ if (!p) return -ENOMEM;
*p = 0x80;
- memset(p+1, 0, padlen - 1);
DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
@@ -1414,11 +1425,10 @@ static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, cha
if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH;
- p = kmalloc(padlen, alloc_flag);
- if (!pad) return -ENOMEM;
+ p = kzalloc(padlen, alloc_flag);
+ if (!p) return -ENOMEM;
*p = 0x80;
- memset(p+1, 0, padlen - 1);
DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
@@ -1886,14 +1896,14 @@ static void cryptocop_do_tasklet(unsigned long unused)
}
static irqreturn_t
-dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+dma_done_interrupt(int irq, void *dev_id)
{
struct cryptocop_prio_job *done_job;
reg_dma_rw_ack_intr ack_intr = {
.data = 1,
};
- REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr);
+ REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);
DEBUG(printk("cryptocop DMA done\n"));
@@ -1937,7 +1947,6 @@ dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs)
static int init_cryptocop(void)
{
unsigned long flags;
- reg_intr_vect_rw_mask intr_mask;
reg_dma_rw_cfg dma_cfg = {.en = 1};
reg_dma_rw_intr_mask intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */
reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 };
@@ -1950,10 +1959,14 @@ static int init_cryptocop(void)
.en = 1
};
- if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9");
+ if (request_irq(DMA_IRQ, dma_done_interrupt, 0,
+ "stream co-processor DMA", NULL))
+ panic("request_irq stream co-processor irq dma9");
- (void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
- (void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
+ (void)crisv32_request_dma(OUT_DMA, "strcop", DMA_PANIC_ON_ERROR,
+ 0, dma_strp);
+ (void)crisv32_request_dma(IN_DMA, "strcop", DMA_PANIC_ON_ERROR,
+ 0, dma_strp);
local_irq_save(flags);
@@ -1963,24 +1976,19 @@ static int init_cryptocop(void)
strcop_cfg.en = 1;
REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);
- /* Enable DMA9 interrupt */
- intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
- intr_mask.dma9 = 1;
- REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
-
/* Enable DMAs. */
- REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
- REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+ REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */
+ REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */
/* Set up wordsize = 4 for DMAs. */
- DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4);
- DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4);
+ DMA_WR_CMD(OUT_DMA_INST, regk_dma_set_w_size4);
+ DMA_WR_CMD(IN_DMA_INST, regk_dma_set_w_size4);
/* Enable interrupts. */
- REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+ REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in);
/* Clear intr ack. */
- REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
+ REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);
local_irq_restore(flags);
@@ -1991,7 +1999,6 @@ static int init_cryptocop(void)
static void release_cryptocop(void)
{
unsigned long flags;
- reg_intr_vect_rw_mask intr_mask;
reg_dma_rw_cfg dma_cfg = {.en = 0};
reg_dma_rw_intr_mask intr_mask_in = {0};
reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 };
@@ -1999,26 +2006,21 @@ static void release_cryptocop(void)
local_irq_save(flags);
/* Clear intr ack. */
- REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
-
- /* Disable DMA9 interrupt */
- intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
- intr_mask.dma9 = 0;
- REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+ REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr);
/* Disable DMAs. */
- REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
- REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+ REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */
+ REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */
/* Disable interrupts. */
- REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+ REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in);
local_irq_restore(flags);
- free_irq(DMA9_INTR_VECT, NULL);
+ free_irq(DMA_IRQ, NULL);
- (void)crisv32_free_dma(8);
- (void)crisv32_free_dma(9);
+ (void)crisv32_free_dma(OUT_DMA);
+ (void)crisv32_free_dma(IN_DMA);
}
@@ -2051,7 +2053,6 @@ static void cryptocop_job_queue_close(void)
spin_lock_irqsave(&cryptocop_process_lock, process_flags);
/* Empty the job queue. */
- spin_lock_irqsave(&cryptocop_process_lock, process_flags);
for (i = 0; i < cryptocop_prio_no_prios; i++){
if (!list_empty(&(cryptocop_job_queues[i].jobs))){
list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {
@@ -2077,13 +2078,13 @@ static void cryptocop_job_queue_close(void)
reg_dma_rw_cfg dma_out_cfg, dma_in_cfg;
/* Stop DMA. */
- dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg);
+ dma_out_cfg = REG_RD(dma, OUT_DMA_INST, rw_cfg);
dma_out_cfg.en = regk_dma_no;
- REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg);
+ REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_out_cfg);
- dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg);
+ dma_in_cfg = REG_RD(dma, IN_DMA_INST, rw_cfg);
dma_in_cfg.en = regk_dma_no;
- REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg);
+ REG_WR(dma, IN_DMA_INST, rw_cfg, dma_in_cfg);
/* Disble the cryptocop. */
rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg);
@@ -2227,10 +2228,11 @@ static void cryptocop_start_job(void)
&pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out)));
/* Start input DMA. */
- DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in));
+ flush_dma_context(&pj->iop->ctx_in);
+ DMA_START_CONTEXT(IN_DMA_INST, virt_to_phys(&pj->iop->ctx_in));
/* Start output DMA. */
- DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out));
+ DMA_START_CONTEXT(OUT_DMA_INST, virt_to_phys(&pj->iop->ctx_out));
spin_unlock_irqrestore(&running_job_lock, running_job_flags);
DEBUG(printk("cryptocop_start_job: exiting\n"));
@@ -2299,10 +2301,9 @@ static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_
return 0;
}
-
static int cryptocop_open(struct inode *inode, struct file *filp)
{
- int p = MINOR(inode->i_rdev);
+ int p = iminor(inode);
if (p != CRYPTOCOP_MINOR) return -EINVAL;
@@ -2944,7 +2945,7 @@ static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsig
int spdl_err;
/* Mark output pages dirty. */
spdl_err = set_page_dirty_lock(outpages[i]);
- DEBUG(if (spdl_err)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err));
+ DEBUG(if (spdl_err < 0)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err));
}
for (i = 0; i < nooutpages; i++){
put_page(outpages[i]);
@@ -3098,7 +3099,8 @@ static int cryptocop_ioctl_create_session(struct inode *inode, struct file *filp
return 0;
}
-static int cryptocop_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+static long cryptocop_ioctl_unlocked(struct inode *inode,
+ struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
if (_IOC_TYPE(cmd) != ETRAXCRYPTOCOP_IOCTYPE) {
@@ -3130,6 +3132,18 @@ static int cryptocop_ioctl(struct inode *inode, struct file *filp, unsigned int
return 0;
}
+static long
+cryptocop_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ mutex_lock(&cryptocop_mutex);
+ ret = cryptocop_ioctl_unlocked(file_inode(filp), filp, cmd, arg);
+ mutex_unlock(&cryptocop_mutex);
+
+ return ret;
+}
+
#ifdef LDEBUG
static void print_dma_descriptors(struct cryptocop_int_operation *iop)
diff --git a/arch/cris/arch-v32/drivers/gpio.c b/arch/cris/arch-v32/drivers/gpio.c
deleted file mode 100644
index a551237dcb5..00000000000
--- a/arch/cris/arch-v32/drivers/gpio.c
+++ /dev/null
@@ -1,766 +0,0 @@
-/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $
- *
- * ETRAX CRISv32 general port I/O device
- *
- * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
- *
- * Authors: Bjorn Wesen (initial version)
- * Ola Knutsson (LED handling)
- * Johan Adolfsson (read/set directions, write, port G,
- * port to ETRAX FS.
- *
- * $Log: gpio.c,v $
- * Revision 1.16 2005/06/19 17:06:49 starvik
- * Merge of Linux 2.6.12.
- *
- * Revision 1.15 2005/05/25 08:22:20 starvik
- * Changed GPIO port order to fit packages/devices/axis-2.4.
- *
- * Revision 1.14 2005/04/24 18:35:08 starvik
- * Updated with final register headers.
- *
- * Revision 1.13 2005/03/15 15:43:00 starvik
- * dev_id needs to be supplied for shared IRQs.
- *
- * Revision 1.12 2005/03/10 17:12:00 starvik
- * Protect alarm list with spinlock.
- *
- * Revision 1.11 2005/01/05 06:08:59 starvik
- * No need to do local_irq_disable after local_irq_save.
- *
- * Revision 1.10 2004/11/19 08:38:31 starvik
- * Removed old crap.
- *
- * Revision 1.9 2004/05/14 07:58:02 starvik
- * Merge of changes from 2.4
- *
- * Revision 1.8 2003/09/11 07:29:50 starvik
- * Merge of Linux 2.6.0-test5
- *
- * Revision 1.7 2003/07/10 13:25:46 starvik
- * Compiles for 2.5.74
- * Lindented ethernet.c
- *
- * Revision 1.6 2003/07/04 08:27:46 starvik
- * Merge of Linux 2.5.74
- *
- * Revision 1.5 2003/06/10 08:26:37 johana
- * Etrax -> ETRAX CRISv32
- *
- * Revision 1.4 2003/06/05 14:22:48 johana
- * Initialise some_alarms.
- *
- * Revision 1.3 2003/06/05 10:15:46 johana
- * New INTR_VECT macros.
- * Enable interrupts in global config.
- *
- * Revision 1.2 2003/06/03 15:52:50 johana
- * Initial CRIS v32 version.
- *
- * Revision 1.1 2003/06/03 08:53:15 johana
- * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7.
- *
- */
-
-#include <linux/config.h>
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-
-#include <asm/etraxgpio.h>
-#include <asm/arch/hwregs/reg_map.h>
-#include <asm/arch/hwregs/reg_rdwr.h>
-#include <asm/arch/hwregs/gio_defs.h>
-#include <asm/arch/hwregs/intr_vect_defs.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-
-/* 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 char gpio_name[] = "etrax gpio";
-
-#if 0
-static wait_queue_head_t *gpio_wq;
-#endif
-
-static int gpio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
-static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
- loff_t *off);
-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 = 0;
-
-static int gpio_some_alarms = 0; /* Set if someone uses alarm */
-static unsigned long gpio_pa_high_alarms = 0;
-static unsigned long gpio_pa_low_alarms = 0;
-
-static 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;
-
-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),
-};
-
-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),
-};
-
-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,
-};
-
-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,
-};
-
-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),
-};
-
-
-
-static unsigned int
-gpio_poll(struct file *file,
- poll_table *wait)
-{
- unsigned int mask = 0;
- struct gpio_private *priv = (struct gpio_private *)file->private_data;
- 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("gpio_poll ready: mask 0x%08X\n", mask));
- return mask;
-}
-
-int etrax_gpio_wake_up_check(void)
-{
- struct gpio_private *priv = alarmlist;
- unsigned long data = 0;
- int ret = 0;
- while (priv) {
- data = *data_in[priv->minor];
- if ((data & priv->highalarm) ||
- (~data & priv->lowalarm)) {
- DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
- wake_up_interruptible(&priv->alarm_wq);
- ret = 1;
- }
- priv = priv->next;
- }
- return ret;
-}
-
-static irqreturn_t
-gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- 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, struct pt_regs *regs)
-{
- 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;
-
- /* 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);
-
- /* 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;
- 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 = (struct gpio_private *)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... */
- 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("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 = MINOR(inode->i_rdev);
-
- if (p > GPIO_MINOR_LAST)
- return -EINVAL;
-
- priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
- GFP_KERNEL);
-
- if (!priv)
- return -ENOMEM;
-
- priv->minor = p;
-
- /* initialize the io/alarm struct and link it into our alarmlist */
-
- priv->next = alarmlist;
- alarmlist = priv;
- 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;
-
- return 0;
-}
-
-static int
-gpio_release(struct inode *inode, struct file *filp)
-{
- struct gpio_private *p = alarmlist;
- struct gpio_private *todel = (struct gpio_private *)filp->private_data;
- /* local copies while updating them: */
- unsigned long a_high, a_low;
- unsigned long some_alarms;
-
- /* unlink from alarmlist and free the private structure */
-
- 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) {
- a_high |= p->highalarm;
- a_low |= p->lowalarm;
- }
-
- if (p->highalarm | p->lowalarm) {
- some_alarms = 1;
- }
- p = p->next;
- }
-
- spin_lock(&alarm_lock);
- gpio_some_alarms = some_alarms;
- gpio_pa_high_alarms = a_high;
- gpio_pa_low_alarms = a_low;
- spin_unlock(&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().
- */
-
-unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
-{
- /* Set direction 0=unchanged 1=input,
- * return mask with 1=input
- */
- unsigned long flags;
- 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 */
- else
- dir_shadow ^= 0x3FFFF; /* Only 18 bits */
- return dir_shadow;
-
-} /* setget_input */
-
-unsigned long inline 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(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
- unsigned long val;
- unsigned long shadow;
- struct gpio_private *priv = (struct gpio_private *)file->private_data;
- if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
- return -EINVAL;
- }
-
- 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);
- if (arg & 0x04)
- printk("GPIO SET 2\n");
- // 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);
- if (arg & 0x04)
- printk("GPIO CLR 2\n");
- // 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(&alarm_lock);
- gpio_some_alarms = 1;
- if (priv->minor == GPIO_MINOR_A) {
- gpio_pa_high_alarms |= arg;
- }
- 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;
- if (priv->minor == GPIO_MINOR_A) {
- gpio_pa_low_alarms |= arg;
- }
- 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);
- 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(&alarm_lock);
- 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 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;
- LED_ACTIVE_SET_G(green);
- LED_ACTIVE_SET_R(red);
- break;
-
- default:
- return -EINVAL;
- } /* switch */
-
- return 0;
-}
-
-struct file_operations gpio_fops = {
- .owner = THIS_MODULE,
- .poll = gpio_poll,
- .ioctl = gpio_ioctl,
- .write = gpio_write,
- .open = gpio_open,
- .release = gpio_release,
-};
-
-
-/* main driver initialization routine, called from mem.c */
-
-static __init int
-gpio_init(void)
-{
- int res;
- reg_intr_vect_rw_mask intr_mask;
-
- /* 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 */
- LED_NETWORK_SET(0);
- LED_ACTIVE_SET(0);
- LED_DISK_READ(0);
- LED_DISK_WRITE(0);
-
- printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n");
- /* We call etrax_gpio_wake_up_check() from timer interrupt and
- * from cpu_idle() in kernel/process.c
- * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
- * in some tests.
- */
- if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt,
- SA_SHIRQ | SA_INTERRUPT,"gpio poll", &alarmlist)) {
- printk("err: timer0 irq for gpio\n");
- }
- if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt,
- SA_SHIRQ | SA_INTERRUPT,"gpio PA", &alarmlist)) {
- printk("err: PA irq for gpio\n");
- }
- /* enable the gio and timer irq in global config */
- intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
- intr_mask.timer = 1;
- intr_mask.gen_io = 1;
- REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
-
- return res;
-}
-
-/* this makes sure that gpio_init is called during kernel boot */
-
-module_init(gpio_init);
diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c
index 440c20a9496..3b2c82ce814 100644
--- a/arch/cris/arch-v32/drivers/i2c.c
+++ b/arch/cris/arch-v32/drivers/i2c.c
@@ -19,25 +19,23 @@
*!
*! ---------------------------------------------------------------------------
*!
-*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
+*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/
-/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
+
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
-#include <linux/config.h>
+#include <linux/mutex.h>
#include <asm/etraxi2c.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/delay.h>
@@ -48,6 +46,7 @@
#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
@@ -80,6 +79,8 @@ static const char i2c_name[] = "i2c";
#define i2c_delay(usecs) udelay(usecs)
+static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
+
/****************** VARIABLE SECTION ************************************/
static struct crisv32_iopin cris_i2c_clk;
@@ -253,6 +254,7 @@ i2c_getack(void)
* generate ACK clock pulse
*/
i2c_clk(I2C_CLOCK_HIGH);
+#if 0
/*
* Use PORT PB instead of I2C
* for input. (I2C not working)
@@ -265,6 +267,8 @@ i2c_getack(void)
i2c_data(1);
i2c_disable();
i2c_dir_in();
+#endif
+
/*
* now wait for ack
*/
@@ -272,11 +276,11 @@ i2c_getack(void)
/*
* check for ack
*/
- if(i2c_getbit())
+ if (i2c_getbit())
ack = 0;
i2c_delay(CLOCK_HIGH_TIME/2);
- if(!ack){
- if(!i2c_getbit()) /* receiver pulld SDA low */
+ if (!ack) {
+ if (!i2c_getbit()) /* receiver pulld SDA low */
ack = 1;
i2c_delay(CLOCK_HIGH_TIME/2);
}
@@ -286,6 +290,7 @@ i2c_getack(void)
* before we enable our output. If we keep data high
* and enable output, we would generate a stop condition.
*/
+#if 0
i2c_data(I2C_DATA_LOW);
/*
@@ -293,6 +298,7 @@ i2c_getack(void)
*/
i2c_enable();
i2c_dir_out();
+#endif
i2c_clk(I2C_CLOCK_LOW);
i2c_delay(CLOCK_HIGH_TIME/4);
/*
@@ -376,6 +382,121 @@ i2c_sendnack(void)
/*#---------------------------------------------------------------------------
*#
+*# FUNCTION NAME: i2c_write
+*#
+*# DESCRIPTION : Writes a value to an I2C device
+*#
+*#--------------------------------------------------------------------------*/
+int
+i2c_write(unsigned char theSlave, void *data, size_t nbytes)
+{
+ int error, cntr = 3;
+ unsigned char bytes_wrote = 0;
+ unsigned char value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_lock, flags);
+
+ do {
+ error = 0;
+
+ i2c_start();
+ /*
+ * send slave address
+ */
+ i2c_outbyte((theSlave & 0xfe));
+ /*
+ * wait for ack
+ */
+ if (!i2c_getack())
+ error = 1;
+ /*
+ * send data
+ */
+ for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) {
+ memcpy(&value, data + bytes_wrote, sizeof value);
+ i2c_outbyte(value);
+ /*
+ * now it's time to wait for ack
+ */
+ if (!i2c_getack())
+ error |= 4;
+ }
+ /*
+ * end byte stream
+ */
+ i2c_stop();
+
+ } while (error && cntr--);
+
+ i2c_delay(CLOCK_LOW_TIME);
+
+ spin_unlock_irqrestore(&i2c_lock, flags);
+
+ return -error;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_read
+*#
+*# DESCRIPTION : Reads a value from an I2C device
+*#
+*#--------------------------------------------------------------------------*/
+int
+i2c_read(unsigned char theSlave, void *data, size_t nbytes)
+{
+ unsigned char b = 0;
+ unsigned char bytes_read = 0;
+ int error, cntr = 3;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_lock, flags);
+
+ do {
+ error = 0;
+ memset(data, 0, nbytes);
+ /*
+ * generate start condition
+ */
+ i2c_start();
+ /*
+ * send slave address
+ */
+ i2c_outbyte((theSlave | 0x01));
+ /*
+ * wait for ack
+ */
+ if (!i2c_getack())
+ error = 1;
+ /*
+ * fetch data
+ */
+ for (bytes_read = 0; bytes_read < nbytes; bytes_read++) {
+ b = i2c_inbyte();
+ memcpy(data + bytes_read, &b, sizeof b);
+
+ if (bytes_read < (nbytes - 1))
+ i2c_sendack();
+ }
+ /*
+ * last received byte needs to be nacked
+ * instead of acked
+ */
+ i2c_sendnack();
+ /*
+ * end sequence
+ */
+ i2c_stop();
+ } while (error && cntr--);
+
+ spin_unlock_irqrestore(&i2c_lock, flags);
+
+ return -error;
+}
+
+/*#---------------------------------------------------------------------------
+*#
*# FUNCTION NAME: i2c_writereg
*#
*# DESCRIPTION : Writes a value to an I2C device
@@ -388,12 +509,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
int error, cntr = 3;
unsigned long flags;
+ spin_lock_irqsave(&i2c_lock, flags);
+
do {
error = 0;
- /*
- * we don't like to be interrupted
- */
- local_irq_save(flags);
i2c_start();
/*
@@ -428,15 +547,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
* end byte stream
*/
i2c_stop();
- /*
- * enable interrupt again
- */
- local_irq_restore(flags);
-
} while(error && cntr--);
i2c_delay(CLOCK_LOW_TIME);
+ spin_unlock_irqrestore(&i2c_lock, flags);
+
return -error;
}
@@ -454,13 +570,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
int error, cntr = 3;
unsigned long flags;
+ spin_lock_irqsave(&i2c_lock, flags);
+
do {
error = 0;
/*
- * we don't like to be interrupted
- */
- local_irq_save(flags);
- /*
* generate start condition
*/
i2c_start();
@@ -483,7 +597,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
* now it's time to wait for ack
*/
if(!i2c_getack())
- error = 1;
+ error |= 2;
/*
* repeat start condition
*/
@@ -497,7 +611,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
* wait for ack
*/
if(!i2c_getack())
- error = 1;
+ error |= 4;
/*
* fetch register
*/
@@ -511,13 +625,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
* end sequence
*/
i2c_stop();
- /*
- * enable interrupt again
- */
- local_irq_restore(flags);
} while(error && cntr--);
+ spin_unlock_irqrestore(&i2c_lock, flags);
+
return b;
}
@@ -536,12 +648,12 @@ i2c_release(struct inode *inode, struct file *filp)
/* Main device API. ioctl's to write or read to/from i2c registers.
*/
-static int
-i2c_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long
+i2c_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ int ret;
if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
- return -EINVAL;
+ return -ENOTTY;
}
switch (_IOC_NR(cmd)) {
@@ -552,9 +664,13 @@ i2c_ioctl(struct inode *inode, struct file *file,
I2C_ARGREG(arg),
I2C_ARGVALUE(arg)));
- return i2c_writereg(I2C_ARGSLAVE(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;
@@ -562,7 +678,9 @@ i2c_ioctl(struct inode *inode, struct file *file,
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;
}
@@ -574,38 +692,60 @@ i2c_ioctl(struct inode *inode, struct file *file,
return 0;
}
-static struct file_operations i2c_fops = {
- owner: THIS_MODULE,
- ioctl: i2c_ioctl,
- open: i2c_open,
- release: i2c_release,
+static const struct file_operations i2c_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = i2c_ioctl,
+ .open = i2c_open,
+ .release = i2c_release,
+ .llseek = noop_llseek,
};
-int __init
-i2c_init(void)
+static int __init i2c_init(void)
{
- int res;
+ static int res;
+ static int first = 1;
- /* Setup and enable the Port B I2C interface */
+ if (!first)
+ return res;
+
+ 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;
+}
- crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
- crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
+
+static int __init i2c_register(void)
+{
+ int res;
+
+ res = i2c_init();
+ if (res < 0)
+ return res;
/* register char device */
res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
- if(res < 0) {
+ if (res < 0) {
printk(KERN_ERR "i2c: couldn't get a major number.\n");
return res;
}
- printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+ printk(KERN_INFO
+ "I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n");
return 0;
}
-
/* this makes sure that i2c_init is called during boot */
-
-module_init(i2c_init);
+module_init(i2c_register);
/****************** END OF FILE i2c.c ********************************/
diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h
index bfe1a13f9f3..c073cf4ba01 100644
--- a/arch/cris/arch-v32/drivers/i2c.h
+++ b/arch/cris/arch-v32/drivers/i2c.h
@@ -3,6 +3,8 @@
/* High level I2C actions */
int __init i2c_init(void);
+int i2c_write(unsigned char theSlave, void *data, size_t nbytes);
+int i2c_read(unsigned char theSlave, void *data, size_t nbytes);
int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
diff --git a/arch/cris/arch-v32/drivers/iop_fw_load.c b/arch/cris/arch-v32/drivers/iop_fw_load.c
index 11f9895ded5..2f8ea0f7a63 100644
--- a/arch/cris/arch-v32/drivers/iop_fw_load.c
+++ b/arch/cris/arch-v32/drivers/iop_fw_load.c
@@ -1,5 +1,4 @@
-/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $
- *
+/*
* Firmware loader for ETRAX FS IO-Processor
*
* Copyright (C) 2004 Axis Communications AB
@@ -11,22 +10,26 @@
#include <linux/device.h>
#include <linux/firmware.h>
-#include <asm/arch/hwregs/reg_map.h>
-#include <asm/arch/hwregs/iop/iop_reg_space.h>
-#include <asm/arch/hwregs/iop/iop_mpu_macros.h>
-#include <asm/arch/hwregs/iop/iop_mpu_defs.h>
-#include <asm/arch/hwregs/iop/iop_spu_defs.h>
-#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/iop/iop_reg_space.h>
+#include <hwregs/iop/iop_mpu_macros.h>
+#include <hwregs/iop/iop_mpu_defs.h>
+#include <hwregs/iop/iop_spu_defs.h>
+#include <hwregs/iop/iop_sw_cpu_defs.h>
#define IOP_TIMEOUT 100
+#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] = {
- { .bus_id = "iop-spu0", },
- { .bus_id = "iop-spu1", },
+ { .init_name = "iop-spu0", },
+ { .init_name = "iop-spu1", },
};
static struct device iop_mpu_device = {
- .bus_id = "iop-mpu",
+ .init_name = "iop-mpu",
};
static int wait_mpu_idle(void)
@@ -192,6 +195,13 @@ int iop_start_mpu(unsigned int start_addr)
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);
@@ -201,6 +211,7 @@ static int __init iop_fw_load_init(void)
device_initialize(&iop_mpu_device);
kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
kobject_add(&iop_mpu_device.kobj);
+#endif
return 0;
}
diff --git a/arch/cris/arch-v32/drivers/mach-a3/Makefile b/arch/cris/arch-v32/drivers/mach-a3/Makefile
new file mode 100644
index 00000000000..5c6d2a2a080
--- /dev/null
+++ b/arch/cris/arch-v32/drivers/mach-a3/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Etrax-specific drivers
+#
+
+obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
+obj-$(CONFIG_ETRAX_GPIO) += gpio.o
diff --git a/arch/cris/arch-v32/drivers/mach-a3/gpio.c b/arch/cris/arch-v32/drivers/mach-a3/gpio.c
new file mode 100644
index 00000000000..74f9fe80940
--- /dev/null
+++ b/arch/cris/arch-v32/drivers/mach-a3/gpio.c
@@ -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);
diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c
new file mode 100644
index 00000000000..7fb52128ddc
--- /dev/null
+++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c
@@ -0,0 +1,180 @@
+/*
+ * arch/cris/arch-v32/drivers/nandflash.c
+ *
+ * Copyright (c) 2007
+ *
+ * Derived from drivers/mtd/nand/spia.c
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <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;
+}
+
diff --git a/arch/cris/arch-v32/drivers/mach-fs/Makefile b/arch/cris/arch-v32/drivers/mach-fs/Makefile
new file mode 100644
index 00000000000..5c6d2a2a080
--- /dev/null
+++ b/arch/cris/arch-v32/drivers/mach-fs/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Etrax-specific drivers
+#
+
+obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
+obj-$(CONFIG_ETRAX_GPIO) += gpio.o
diff --git a/arch/cris/arch-v32/drivers/mach-fs/gpio.c b/arch/cris/arch-v32/drivers/mach-fs/gpio.c
new file mode 100644
index 00000000000..009f4ee1bd0
--- /dev/null
+++ b/arch/cris/arch-v32/drivers/mach-fs/gpio.c
@@ -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);
diff --git a/arch/cris/arch-v32/drivers/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c
index 93ddea4d956..e03238454b0 100644
--- a/arch/cris/arch-v32/drivers/nandflash.c
+++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c
@@ -4,9 +4,7 @@
* Copyright (c) 2004
*
* Derived from drivers/mtd/nand/spia.c
- * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
- *
- * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,11 +18,11 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
-#include <asm/arch/memmap.h>
-#include <asm/arch/hwregs/reg_map.h>
-#include <asm/arch/hwregs/reg_rdwr.h>
-#include <asm/arch/hwregs/gio_defs.h>
-#include <asm/arch/hwregs/bif_core_defs.h>
+#include <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
@@ -32,44 +30,65 @@
#define ALE_BIT 6
#define BY_BIT 7
-static struct mtd_info *crisv32_mtd = NULL;
+struct mtd_info_wrapper {
+ struct mtd_info info;
+ struct nand_chip chip;
+};
+
+/* Bitmask for control pins */
+#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT))
+
+/* Bitmask for mtd nand control bits */
+#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE)
+
+
+static struct mtd_info *crisv32_mtd;
/*
* hardware specific access to control-lines
-*/
-static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd)
+ */
+static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
{
unsigned long flags;
- reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout);
+ reg_gio_rw_pa_dout dout;
+ struct nand_chip *this = mtd->priv;
local_irq_save(flags);
- switch(cmd){
- case NAND_CTL_SETCLE:
- dout.data |= (1<<CLE_BIT);
- break;
- case NAND_CTL_CLRCLE:
- dout.data &= ~(1<<CLE_BIT);
- break;
- case NAND_CTL_SETALE:
- dout.data |= (1<<ALE_BIT);
- break;
- case NAND_CTL_CLRALE:
- dout.data &= ~(1<<ALE_BIT);
- break;
- case NAND_CTL_SETNCE:
- dout.data |= (1<<CE_BIT);
- break;
- case NAND_CTL_CLRNCE:
- dout.data &= ~(1<<CE_BIT);
- break;
+
+ /* control bits change */
+ if (ctrl & NAND_CTRL_CHANGE) {
+ dout = REG_RD(gio, regi_gio, rw_pa_dout);
+ dout.data &= ~PIN_BITMASK;
+
+#if (CE_BIT == 4 && NAND_NCE == 1 && \
+ CLE_BIT == 5 && NAND_CLE == 2 && \
+ ALE_BIT == 6 && NAND_ALE == 4)
+ /* Pins in same order as control bits, but shifted.
+ * Optimize for this case; works for 2.6.18 */
+ dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT;
+#else
+ /* the slow way */
+ if (!(ctrl & NAND_NCE))
+ dout.data |= (1 << CE_BIT);
+ if (ctrl & NAND_CLE)
+ dout.data |= (1 << CLE_BIT);
+ if (ctrl & NAND_ALE)
+ dout.data |= (1 << ALE_BIT);
+#endif
+ REG_WR(gio, regi_gio, rw_pa_dout, dout);
}
- REG_WR(gio, regi_gio, rw_pa_dout, dout);
+
+ /* command to chip */
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+
local_irq_restore(flags);
}
/*
* read device ready pin
*/
-int crisv32_device_ready(struct mtd_info *mtd)
+static int crisv32_device_ready(struct mtd_info *mtd)
{
reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din);
return ((din.data & (1 << BY_BIT)) >> BY_BIT);
@@ -78,21 +97,23 @@ int crisv32_device_ready(struct mtd_info *mtd)
/*
* Main initialization routine
*/
-struct mtd_info* __init crisv32_nand_flash_probe (void)
+struct mtd_info *__init crisv32_nand_flash_probe(void)
{
void __iomem *read_cs;
void __iomem *write_cs;
- reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg);
+ reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core,
+ rw_grp3_cfg);
reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe);
+ struct mtd_info_wrapper *wrapper;
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
- crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
- GFP_KERNEL);
- if (!crisv32_mtd) {
- printk ("Unable to allocate CRISv32 NAND MTD device structure.\n");
+ wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL);
+ if (!wrapper) {
+ printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD "
+ "device structure.\n");
err = -ENOMEM;
return NULL;
}
@@ -101,45 +122,42 @@ struct mtd_info* __init crisv32_nand_flash_probe (void)
write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192);
if (!read_cs || !write_cs) {
- printk("CRISv32 NAND ioremap failed\n");
+ printk(KERN_ERR "CRISv32 NAND ioremap failed\n");
err = -EIO;
goto out_mtd;
}
/* Get pointer to private data */
- this = (struct nand_chip *) (&crisv32_mtd[1]);
+ this = &wrapper->chip;
+ crisv32_mtd = &wrapper->info;
pa_oe.oe |= 1 << CE_BIT;
pa_oe.oe |= 1 << ALE_BIT;
pa_oe.oe |= 1 << CLE_BIT;
- pa_oe.oe &= ~ (1 << BY_BIT);
+ pa_oe.oe &= ~(1 << BY_BIT);
REG_WR(gio, regi_gio, rw_pa_oe, pa_oe);
bif_cfg.gated_csp0 = regk_bif_core_rd;
bif_cfg.gated_csp1 = regk_bif_core_wr;
REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg);
- /* Initialize structures */
- memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info));
- memset((char *) this, 0, sizeof(struct nand_chip));
-
/* Link the private data with the MTD structure */
crisv32_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
this->IO_ADDR_W = write_cs;
- this->hwcontrol = crisv32_hwcontrol;
+ this->cmd_ctrl = crisv32_hwcontrol;
this->dev_ready = crisv32_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
- this->eccmode = NAND_ECC_SOFT;
+ this->ecc.mode = NAND_ECC_SOFT;
/* Enable the following for a flash based bad block table */
- this->options = NAND_USE_FLASH_BBT;
+ /* this->bbt_options = NAND_BBT_USE_FLASH; */
- /* Scan to find existance of the device */
- if (nand_scan (crisv32_mtd, 1)) {
+ /* Scan to find existence of the device */
+ if (nand_scan(crisv32_mtd, 1)) {
err = -ENXIO;
goto out_ior;
}
@@ -150,7 +168,7 @@ out_ior:
iounmap((void *)read_cs);
iounmap((void *)write_cs);
out_mtd:
- kfree (crisv32_mtd);
- return NULL;
+ kfree(wrapper);
+ return NULL;
}
diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c
deleted file mode 100644
index d788bda3578..00000000000
--- a/arch/cris/arch-v32/drivers/pcf8563.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * PCF8563 RTC
- *
- * From Phillips' datasheet:
- *
- * The PCF8563 is a CMOS real-time clock/calendar optimized for low power
- * consumption. A programmable clock output, interupt output and voltage
- * low detector are also provided. All address and data are transferred
- * serially via two-line bidirectional I2C-bus. Maximum bus speed is
- * 400 kbits/s. The built-in word address register is incremented
- * automatically after each written or read byte.
- *
- * Copyright (c) 2002-2003, Axis Communications AB
- * All rights reserved.
- *
- * Author: Tobias Anderberg <tobiasa@axis.com>.
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <linux/delay.h>
-#include <linux/bcd.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/rtc.h>
-
-#include "i2c.h"
-
-#define PCF8563_MAJOR 121 /* Local major number. */
-#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */
-#define PCF8563_NAME "PCF8563"
-#define DRIVER_VERSION "$Revision: 1.1 $"
-
-/* Two simple wrapper macros, saves a few keystrokes. */
-#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
-#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
-
-static const unsigned char days_in_month[] =
- { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
-int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-int pcf8563_open(struct inode *, struct file *);
-int pcf8563_release(struct inode *, struct file *);
-
-static struct file_operations pcf8563_fops = {
- owner: THIS_MODULE,
- ioctl: pcf8563_ioctl,
- open: pcf8563_open,
- release: pcf8563_release,
-};
-
-unsigned char
-pcf8563_readreg(int reg)
-{
- unsigned char res = rtc_read(reg);
-
- /* The PCF8563 does not return 0 for unimplemented bits */
- switch (reg) {
- case RTC_SECONDS:
- case RTC_MINUTES:
- res &= 0x7F;
- break;
- case RTC_HOURS:
- case RTC_DAY_OF_MONTH:
- res &= 0x3F;
- break;
- case RTC_WEEKDAY:
- res &= 0x07;
- break;
- case RTC_MONTH:
- res &= 0x1F;
- break;
- case RTC_CONTROL1:
- res &= 0xA8;
- break;
- case RTC_CONTROL2:
- res &= 0x1F;
- break;
- case RTC_CLOCKOUT_FREQ:
- case RTC_TIMER_CONTROL:
- res &= 0x83;
- break;
- }
- return res;
-}
-
-void
-pcf8563_writereg(int reg, unsigned char val)
-{
-#ifdef CONFIG_ETRAX_RTC_READONLY
- if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
- return;
-#endif
-
- rtc_write(reg, val);
-}
-
-void
-get_rtc_time(struct rtc_time *tm)
-{
- tm->tm_sec = rtc_read(RTC_SECONDS);
- tm->tm_min = rtc_read(RTC_MINUTES);
- tm->tm_hour = rtc_read(RTC_HOURS);
- tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
- tm->tm_wday = rtc_read(RTC_WEEKDAY);
- tm->tm_mon = rtc_read(RTC_MONTH);
- tm->tm_year = rtc_read(RTC_YEAR);
-
- if (tm->tm_sec & 0x80)
- printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
- "information is no longer guaranteed!\n", PCF8563_NAME);
-
- tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
- tm->tm_sec &= 0x7F;
- tm->tm_min &= 0x7F;
- tm->tm_hour &= 0x3F;
- tm->tm_mday &= 0x3F;
- tm->tm_wday &= 0x07; /* Not coded in BCD. */
- tm->tm_mon &= 0x1F;
-
- BCD_TO_BIN(tm->tm_sec);
- BCD_TO_BIN(tm->tm_min);
- BCD_TO_BIN(tm->tm_hour);
- BCD_TO_BIN(tm->tm_mday);
- BCD_TO_BIN(tm->tm_mon);
- tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */
-}
-
-int __init
-pcf8563_init(void)
-{
- /* Initiate the i2c protocol. */
- i2c_init();
-
- /*
- * First of all we need to reset the chip. This is done by
- * clearing control1, control2 and clk freq and resetting
- * all alarms.
- */
- if (rtc_write(RTC_CONTROL1, 0x00) < 0)
- goto err;
-
- if (rtc_write(RTC_CONTROL2, 0x00) < 0)
- goto err;
-
- if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
- goto err;
-
- if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
- goto err;
-
- /* Reset the alarms. */
- if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
- goto err;
-
- if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
- goto err;
-
- if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
- goto err;
-
- if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
- goto err;
-
- if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
- printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
- PCF8563_NAME, PCF8563_MAJOR);
- return -1;
- }
-
- printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
-
- /* Check for low voltage, and warn about it.. */
- if (rtc_read(RTC_SECONDS) & 0x80)
- printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
- "information is no longer guaranteed!\n", PCF8563_NAME);
-
- return 0;
-
-err:
- printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
- return -1;
-}
-
-void __exit
-pcf8563_exit(void)
-{
- if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) {
- printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME);
- }
-}
-
-/*
- * ioctl calls for this driver. Why return -ENOTTY upon error? Because
- * POSIX says so!
- */
-int
-pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
-{
- /* Some sanity checks. */
- if (_IOC_TYPE(cmd) != RTC_MAGIC)
- return -ENOTTY;
-
- if (_IOC_NR(cmd) > RTC_MAX_IOCTL)
- return -ENOTTY;
-
- switch (cmd) {
- case RTC_RD_TIME:
- {
- struct rtc_time tm;
-
- memset(&tm, 0, sizeof (struct rtc_time));
- get_rtc_time(&tm);
-
- if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
- return -EFAULT;
- }
-
- return 0;
- }
-
- case RTC_SET_TIME:
- {
-#ifdef CONFIG_ETRAX_RTC_READONLY
- return -EPERM;
-#else
- int leap;
- int year;
- int century;
- struct rtc_time tm;
-
- if (!capable(CAP_SYS_TIME))
- return -EPERM;
-
- if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
- return -EFAULT;
-
- /* Convert from struct tm to struct rtc_time. */
- tm.tm_year += 1900;
- tm.tm_mon += 1;
-
- /*
- * Check if tm.tm_year is a leap year. A year is a leap
- * year if it is divisible by 4 but not 100, except
- * that years divisible by 400 _are_ leap years.
- */
- year = tm.tm_year;
- leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
-
- /* Perform some sanity checks. */
- if ((tm.tm_year < 1970) ||
- (tm.tm_mon > 12) ||
- (tm.tm_mday == 0) ||
- (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
- (tm.tm_wday >= 7) ||
- (tm.tm_hour >= 24) ||
- (tm.tm_min >= 60) ||
- (tm.tm_sec >= 60))
- return -EINVAL;
-
- century = (tm.tm_year >= 2000) ? 0x80 : 0;
- tm.tm_year = tm.tm_year % 100;
-
- BIN_TO_BCD(tm.tm_year);
- BIN_TO_BCD(tm.tm_mday);
- BIN_TO_BCD(tm.tm_hour);
- BIN_TO_BCD(tm.tm_min);
- BIN_TO_BCD(tm.tm_sec);
- tm.tm_mon |= century;
-
- rtc_write(RTC_YEAR, tm.tm_year);
- rtc_write(RTC_MONTH, tm.tm_mon);
- rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
- rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
- rtc_write(RTC_HOURS, tm.tm_hour);
- rtc_write(RTC_MINUTES, tm.tm_min);
- rtc_write(RTC_SECONDS, tm.tm_sec);
-
- return 0;
-#endif /* !CONFIG_ETRAX_RTC_READONLY */
- }
-
- case RTC_VLOW_RD:
- {
- int vl_bit = 0;
-
- if (rtc_read(RTC_SECONDS) & 0x80) {
- vl_bit = 1;
- printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
- "date/time information is no longer guaranteed!\n",
- PCF8563_NAME);
- }
- if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
- return -EFAULT;
-
- return 0;
- }
-
- case RTC_VLOW_SET:
- {
- /* Clear the VL bit in the seconds register */
- int ret = rtc_read(RTC_SECONDS);
-
- rtc_write(RTC_SECONDS, (ret & 0x7F));
-
- return 0;
- }
-
- default:
- return -ENOTTY;
- }
-
- return 0;
-}
-
-int
-pcf8563_open(struct inode *inode, struct file *filp)
-{
- MOD_INC_USE_COUNT;
- return 0;
-}
-
-int
-pcf8563_release(struct inode *inode, struct file *filp)
-{
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-module_init(pcf8563_init);
-module_exit(pcf8563_exit);
diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c
index 24bc149889b..64a5fb93767 100644
--- a/arch/cris/arch-v32/drivers/pci/bios.c
+++ b/arch/cris/arch-v32/drivers/pci/bios.c
@@ -1,16 +1,11 @@
#include <linux/pci.h>
#include <linux/kernel.h>
-#include <asm/arch/hwregs/intr_vect.h>
+#include <arch/hwregs/intr_vect.h>
-void __devinit pcibios_fixup_bus(struct pci_bus *b)
+void pcibios_fixup_bus(struct pci_bus *b)
{
}
-char * __devinit pcibios_setup(char *str)
-{
- return NULL;
-}
-
void pcibios_set_master(struct pci_dev *dev)
{
u8 lat;
@@ -27,8 +22,6 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
/* Leave vm_pgoff as-is, the PCI space address is the physical
* address on this platform.
*/
- vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
-
prot = pgprot_val(vma->vm_page_prot);
vma->vm_page_prot = __pgprot(prot);
@@ -43,18 +36,16 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
return 0;
}
-void
-pcibios_align_resource(void *data, struct resource *res,
- unsigned long size, unsigned long align)
+resource_size_t
+pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
{
- if (res->flags & IORESOURCE_IO) {
- unsigned long start = res->start;
+ resource_size_t start = res->start;
- if (start & 0x300) {
- start = (start + 0x3ff) & ~0x3ff;
- res->start = start;
- }
- }
+ if ((res->flags & IORESOURCE_IO) && (start & 0x300))
+ start = (start + 0x3ff) & ~0x3ff;
+
+ return start;
}
int pcibios_enable_resources(struct pci_dev *dev, int mask)
@@ -102,30 +93,7 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
if ((err = pcibios_enable_resources(dev, mask)) < 0)
return err;
- return pcibios_enable_irq(dev);
-}
-
-int pcibios_assign_resources(void)
-{
- struct pci_dev *dev = NULL;
- int idx;
- struct resource *r;
-
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- int class = dev->class >> 8;
-
- /* Don't touch classless devices and host bridges */
- if (!class || class == PCI_CLASS_BRIDGE_HOST)
- continue;
-
- for(idx=0; idx<6; idx++) {
- r = &dev->resource[idx];
-
- if (!r->start && r->end)
- pci_assign_resource(dev, idx);
- }
- }
+ if (!dev->msi_enabled)
+ pcibios_enable_irq(dev);
return 0;
}
-
-EXPORT_SYMBOL(pcibios_assign_resources);
diff --git a/arch/cris/arch-v32/drivers/pci/dma.c b/arch/cris/arch-v32/drivers/pci/dma.c
index 426b09878a0..ee55578d983 100644
--- a/arch/cris/arch-v32/drivers/pci/dma.c
+++ b/arch/cris/arch-v32/drivers/pci/dma.c
@@ -13,37 +13,19 @@
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/pci.h>
+#include <linux/gfp.h>
#include <asm/io.h>
-struct dma_coherent_mem {
- void *virt_base;
- u32 device_base;
- int size;
- int flags;
- unsigned long *bitmap;
-};
-
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
void *ret;
- struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
int order = get_order(size);
/* ignore region specifiers */
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
- if (mem) {
- int page = bitmap_find_free_region(mem->bitmap, mem->size,
- order);
- if (page >= 0) {
- *dma_handle = mem->device_base + (page << PAGE_SHIFT);
- ret = mem->virt_base + (page << PAGE_SHIFT);
- memset(ret, 0, size);
- return ret;
- }
- if (mem->flags & DMA_MEMORY_EXCLUSIVE)
- return NULL;
- }
+ if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
+ return ret;
if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
gfp |= GFP_DMA;
@@ -60,90 +42,9 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
void dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
- struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
int order = get_order(size);
- if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) {
- int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
-
- bitmap_release_region(mem->bitmap, page, order);
- } else
+ if (!dma_release_from_coherent(dev, order, vaddr))
free_pages((unsigned long)vaddr, order);
}
-int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
- dma_addr_t device_addr, size_t size, int flags)
-{
- void __iomem *mem_base;
- int pages = size >> PAGE_SHIFT;
- int bitmap_size = (pages + 31)/32;
-
- if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
- goto out;
- if (!size)
- goto out;
- if (dev->dma_mem)
- goto out;
-
- /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
-
- mem_base = ioremap(bus_addr, size);
- if (!mem_base)
- goto out;
-
- dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
- if (!dev->dma_mem)
- goto out;
- memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem));
- dev->dma_mem->bitmap = kmalloc(bitmap_size, GFP_KERNEL);
- if (!dev->dma_mem->bitmap)
- goto free1_out;
- memset(dev->dma_mem->bitmap, 0, bitmap_size);
-
- dev->dma_mem->virt_base = mem_base;
- dev->dma_mem->device_base = device_addr;
- dev->dma_mem->size = pages;
- dev->dma_mem->flags = flags;
-
- if (flags & DMA_MEMORY_MAP)
- return DMA_MEMORY_MAP;
-
- return DMA_MEMORY_IO;
-
- free1_out:
- kfree(dev->dma_mem->bitmap);
- out:
- return 0;
-}
-EXPORT_SYMBOL(dma_declare_coherent_memory);
-
-void dma_release_declared_memory(struct device *dev)
-{
- struct dma_coherent_mem *mem = dev->dma_mem;
-
- if(!mem)
- return;
- dev->dma_mem = NULL;
- iounmap(mem->virt_base);
- kfree(mem->bitmap);
- kfree(mem);
-}
-EXPORT_SYMBOL(dma_release_declared_memory);
-
-void *dma_mark_declared_memory_occupied(struct device *dev,
- dma_addr_t device_addr, size_t size)
-{
- struct dma_coherent_mem *mem = dev->dma_mem;
- int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
- int pos, err;
-
- if (!mem)
- return ERR_PTR(-EINVAL);
-
- pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
- err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
- if (err != 0)
- return ERR_PTR(err);
- return mem->virt_base + (pos << PAGE_SHIFT);
-}
-EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
diff --git a/arch/cris/arch-v32/drivers/sync_serial.c b/arch/cris/arch-v32/drivers/sync_serial.c
index c85a6df8558..bbb806b6883 100644
--- a/arch/cris/arch-v32/drivers/sync_serial.c
+++ b/arch/cris/arch-v32/drivers/sync_serial.c
@@ -1,5 +1,5 @@
/*
- * Simple synchronous serial port driver for ETRAX FS.
+ * Simple synchronous serial port driver for ETRAX FS and Artpec-3.
*
* Copyright (c) 2005 Axis Communications AB
*
@@ -9,31 +9,32 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/sched.h>
-#include <linux/slab.h>
+#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
+#include <linux/wait.h>
#include <asm/io.h>
-#include <asm/arch/dma.h>
-#include <asm/arch/pinmux.h>
-#include <asm/arch/hwregs/reg_rdwr.h>
-#include <asm/arch/hwregs/sser_defs.h>
-#include <asm/arch/hwregs/dma_defs.h>
-#include <asm/arch/hwregs/dma.h>
-#include <asm/arch/hwregs/intr_vect_defs.h>
-#include <asm/arch/hwregs/intr_vect.h>
-#include <asm/arch/hwregs/reg_map.h>
+#include <dma.h>
+#include <pinmux.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/sser_defs.h>
+#include <hwregs/dma_defs.h>
+#include <hwregs/dma.h>
+#include <hwregs/intr_vect_defs.h>
+#include <hwregs/intr_vect.h>
+#include <hwregs/reg_map.h>
#include <asm/sync_serial.h>
-/* The receiver is a bit tricky beacuse of the continuous stream of data.*/
+
+/* The receiver is a bit tricky because of the continuous stream of data.*/
/* */
/* Three DMA descriptors are linked together. Each DMA descriptor is */
/* responsible for port->bufchunk of a common buffer. */
@@ -64,8 +65,10 @@
/* words can be handled */
#define IN_BUFFER_SIZE 12288
#define IN_DESCR_SIZE 256
-#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE)
-#define OUT_BUFFER_SIZE 4096
+#define NBR_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE)
+
+#define OUT_BUFFER_SIZE 1024*8
+#define NBR_OUT_DESCR 8
#define DEFAULT_FRAME_RATE 0
#define DEFAULT_WORD_RATE 7
@@ -79,6 +82,8 @@
#define DEBUGPOLL(x)
#define DEBUGRXINT(x)
#define DEBUGTXINT(x)
+#define DEBUGTRDMA(x)
+#define DEBUGOUTBUF(x)
typedef struct sync_port
{
@@ -98,10 +103,11 @@ typedef struct sync_port
int output;
int input;
- volatile unsigned int out_count; /* Remaining bytes for current transfer */
- unsigned char* outp; /* Current position in out_buffer */
- volatile unsigned char* volatile readp; /* Next byte to be read by application */
- volatile unsigned char* volatile writep; /* Next byte to be written by etrax */
+ /* Next byte to be read by application */
+ volatile unsigned char *volatile readp;
+ /* Next byte to be written by etrax */
+ volatile unsigned char *volatile writep;
+
unsigned int in_buffer_size;
unsigned int inbufchunk;
unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32)));
@@ -109,11 +115,30 @@ typedef struct sync_port
unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32)));
struct dma_descr_data* next_rx_desc;
struct dma_descr_data* prev_rx_desc;
+
+ /* Pointer to the first available descriptor in the ring,
+ * unless active_tr_descr == catch_tr_descr and a dma
+ * transfer is active */
+ struct dma_descr_data *active_tr_descr;
+
+ /* Pointer to the first allocated descriptor in the ring */
+ struct dma_descr_data *catch_tr_descr;
+
+ /* Pointer to the descriptor with the current end-of-list */
+ struct dma_descr_data *prev_tr_descr;
int full;
- dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16)));
+ /* Pointer to the first byte being read by DMA
+ * or current position in out_buffer if not using DMA. */
+ unsigned char *out_rd_ptr;
+
+ /* Number of bytes currently locked for being read by DMA */
+ int out_buf_count;
+
+ dma_descr_data in_descr[NBR_IN_DESCR] __attribute__ ((__aligned__(16)));
dma_descr_context in_context __attribute__ ((__aligned__(32)));
- dma_descr_data out_descr __attribute__ ((__aligned__(16)));
+ dma_descr_data out_descr[NBR_OUT_DESCR]
+ __attribute__ ((__aligned__(16)));
dma_descr_context out_context __attribute__ ((__aligned__(32)));
wait_queue_head_t out_wait_q;
wait_queue_head_t in_wait_q;
@@ -121,6 +146,7 @@ typedef struct sync_port
spinlock_t lock;
} sync_port;
+static DEFINE_MUTEX(sync_serial_mutex);
static int etrax_sync_serial_init(void);
static void initialize_port(int portnbr);
static inline int sync_data_avail(struct sync_port *port);
@@ -129,7 +155,7 @@ static int sync_serial_open(struct inode *, struct file*);
static int sync_serial_release(struct inode*, struct file*);
static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);
-static int sync_serial_ioctl(struct inode*, struct file*,
+static int sync_serial_ioctl(struct file *,
unsigned int cmd, unsigned long arg);
static ssize_t sync_serial_write(struct file * file, const char * buf,
size_t count, loff_t *ppos);
@@ -144,11 +170,11 @@ static ssize_t sync_serial_read(struct file *file, char *buf,
#endif
static void send_word(sync_port* port);
-static void start_dma(struct sync_port *port, const char* data, int count);
+static void start_dma_out(struct sync_port *port, const char *data, int count);
static void start_dma_in(sync_port* port);
#ifdef SYNC_SER_DMA
-static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs);
-static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static irqreturn_t tr_interrupt(int irq, void *dev_id);
+static irqreturn_t rx_interrupt(int irq, void *dev_id);
#endif
#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \
@@ -158,22 +184,49 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs);
#define SYNC_SER_MANUAL
#endif
#ifdef SYNC_SER_MANUAL
-static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static irqreturn_t manual_interrupt(int irq, void *dev_id);
+#endif
+
+#ifdef CONFIG_ETRAXFS /* ETRAX FS */
+#define OUT_DMA_NBR 4
+#define IN_DMA_NBR 5
+#define PINMUX_SSER pinmux_sser0
+#define SYNCSER_INST regi_sser0
+#define SYNCSER_INTR_VECT SSER0_INTR_VECT
+#define OUT_DMA_INST regi_dma4
+#define IN_DMA_INST regi_dma5
+#define DMA_OUT_INTR_VECT DMA4_INTR_VECT
+#define DMA_IN_INTR_VECT DMA5_INTR_VECT
+#define REQ_DMA_SYNCSER dma_sser0
+#else /* Artpec-3 */
+#define OUT_DMA_NBR 6
+#define IN_DMA_NBR 7
+#define PINMUX_SSER pinmux_sser
+#define SYNCSER_INST regi_sser
+#define SYNCSER_INTR_VECT SSER_INTR_VECT
+#define OUT_DMA_INST regi_dma6
+#define IN_DMA_INST regi_dma7
+#define DMA_OUT_INTR_VECT DMA6_INTR_VECT
+#define DMA_IN_INTR_VECT DMA7_INTR_VECT
+#define REQ_DMA_SYNCSER dma_sser
#endif
/* The ports */
static struct sync_port ports[]=
{
{
- .regi_sser = regi_sser0,
- .regi_dmaout = regi_dma4,
- .regi_dmain = regi_dma5,
+ .regi_sser = SYNCSER_INST,
+ .regi_dmaout = OUT_DMA_INST,
+ .regi_dmain = IN_DMA_INST,
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
.use_dma = 1,
#else
.use_dma = 0,
#endif
- },
+ }
+#ifdef CONFIG_ETRAXFS
+ ,
+
{
.regi_sser = regi_sser1,
.regi_dmaout = regi_dma6,
@@ -184,36 +237,40 @@ static struct sync_port ports[]=
.use_dma = 0,
#endif
}
+#endif
};
-#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port))
-
-static struct file_operations sync_serial_fops = {
- .owner = THIS_MODULE,
- .write = sync_serial_write,
- .read = sync_serial_read,
- .poll = sync_serial_poll,
- .ioctl = sync_serial_ioctl,
- .open = sync_serial_open,
- .release = sync_serial_release
+#define NBR_PORTS ARRAY_SIZE(ports)
+
+static const struct file_operations sync_serial_fops = {
+ .owner = THIS_MODULE,
+ .write = sync_serial_write,
+ .read = sync_serial_read,
+ .poll = sync_serial_poll,
+ .unlocked_ioctl = sync_serial_ioctl,
+ .open = sync_serial_open,
+ .release = sync_serial_release,
+ .llseek = noop_llseek,
};
static int __init etrax_sync_serial_init(void)
{
ports[0].enabled = 0;
+#ifdef CONFIG_ETRAXFS
ports[1].enabled = 0;
-
- if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 )
- {
- printk("unable to get major for synchronous serial port\n");
+#endif
+ if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial",
+ &sync_serial_fops) < 0) {
+ printk(KERN_WARNING
+ "Unable to get major for synchronous serial port\n");
return -EBUSY;
}
/* Initialize Ports */
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
- if (crisv32_pinmux_alloc_fixed(pinmux_sser0))
- {
- printk("Unable to allocate pins for syncrhronous serial port 0\n");
+ if (crisv32_pinmux_alloc_fixed(PINMUX_SSER)) {
+ printk(KERN_WARNING
+ "Unable to alloc pins for synchronous serial port 0\n");
return -EIO;
}
ports[0].enabled = 1;
@@ -221,33 +278,40 @@ static int __init etrax_sync_serial_init(void)
#endif
#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
- if (crisv32_pinmux_alloc_fixed(pinmux_sser1))
- {
- printk("Unable to allocate pins for syncrhronous serial port 0\n");
+ if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) {
+ printk(KERN_WARNING
+ "Unable to alloc pins for synchronous serial port 0\n");
return -EIO;
}
ports[1].enabled = 1;
initialize_port(1);
#endif
- printk("ETRAX FS synchronous serial port driver\n");
+#ifdef CONFIG_ETRAXFS
+ printk(KERN_INFO "ETRAX FS synchronous serial port driver\n");
+#else
+ printk(KERN_INFO "Artpec-3 synchronous serial port driver\n");
+#endif
return 0;
}
static void __init initialize_port(int portnbr)
{
- struct sync_port* port = &ports[portnbr];
+ int __attribute__((unused)) i;
+ struct sync_port *port = &ports[portnbr];
reg_sser_rw_cfg cfg = {0};
reg_sser_rw_frm_cfg frm_cfg = {0};
reg_sser_rw_tr_cfg tr_cfg = {0};
reg_sser_rw_rec_cfg rec_cfg = {0};
- DEBUG(printk("Init sync serial port %d\n", portnbr));
+ DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr));
port->port_nbr = portnbr;
port->init_irqs = 1;
- port->outp = port->out_buffer;
+ port->out_rd_ptr = port->out_buffer;
+ port->out_buf_count = 0;
+
port->output = 1;
port->input = 0;
@@ -256,7 +320,7 @@ static void __init initialize_port(int portnbr)
port->in_buffer_size = IN_BUFFER_SIZE;
port->inbufchunk = IN_DESCR_SIZE;
port->next_rx_desc = &port->in_descr[0];
- port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1];
+ port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR-1];
port->prev_rx_desc->eol = 1;
init_waitqueue_head(&port->out_wait_q);
@@ -287,8 +351,13 @@ static void __init initialize_port(int portnbr)
tr_cfg.sample_size = 7;
tr_cfg.sh_dir = regk_sser_msbfirst;
tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no;
+#if 0
tr_cfg.rate_ctrl = regk_sser_bulk;
tr_cfg.data_pin_use = regk_sser_dout;
+#else
+ tr_cfg.rate_ctrl = regk_sser_iso;
+ tr_cfg.data_pin_use = regk_sser_dout;
+#endif
tr_cfg.bulk_wspace = 1;
REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
@@ -297,6 +366,27 @@ static void __init initialize_port(int portnbr)
rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no;
rec_cfg.fifo_thr = regk_sser_inf;
REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
+
+#ifdef SYNC_SER_DMA
+ /* Setup the descriptor ring for dma out/transmit. */
+ for (i = 0; i < NBR_OUT_DESCR; i++) {
+ port->out_descr[i].wait = 0;
+ port->out_descr[i].intr = 1;
+ port->out_descr[i].eol = 0;
+ port->out_descr[i].out_eop = 0;
+ port->out_descr[i].next =
+ (dma_descr_data *)virt_to_phys(&port->out_descr[i+1]);
+ }
+
+ /* Create a ring from the list. */
+ port->out_descr[NBR_OUT_DESCR-1].next =
+ (dma_descr_data *)virt_to_phys(&port->out_descr[0]);
+
+ /* Setup context for traversing the ring. */
+ port->active_tr_descr = &port->out_descr[0];
+ port->prev_tr_descr = &port->out_descr[NBR_OUT_DESCR-1];
+ port->catch_tr_descr = &port->out_descr[0];
+#endif
}
static inline int sync_data_avail(struct sync_port *port)
@@ -312,7 +402,7 @@ static inline int sync_data_avail(struct sync_port *port)
* ^rp ^wp ^wp ^rp
*/
- if (end >= start)
+ if (end >= start)
avail = end - start;
else
avail = port->in_buffer_size - (start - end);
@@ -332,7 +422,7 @@ static inline int sync_data_avail_to_end(struct sync_port *port)
* ^rp ^wp ^wp ^rp
*/
- if (end >= start)
+ if (end >= start)
avail = end - start;
else
avail = port->flip + port->in_buffer_size - start;
@@ -341,67 +431,73 @@ static inline int sync_data_avail_to_end(struct sync_port *port)
static int sync_serial_open(struct inode *inode, struct file *file)
{
- int dev = MINOR(inode->i_rdev);
- sync_port* port;
+ int dev = iminor(inode);
+ int ret = -EBUSY;
+ sync_port *port;
reg_dma_rw_cfg cfg = {.en = regk_dma_yes};
reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes};
- DEBUG(printk("Open sync serial port %d\n", dev));
+ mutex_lock(&sync_serial_mutex);
+ DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev));
- if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled)
{
- DEBUG(printk("Invalid minor %d\n", dev));
- return -ENODEV;
+ DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev));
+ ret = -ENODEV;
+ goto out;
}
port = &ports[dev];
/* Allow open this device twice (assuming one reader and one writer) */
if (port->busy == 2)
{
- DEBUG(printk("Device is busy.. \n"));
- return -EBUSY;
+ DEBUG(printk(KERN_DEBUG "Device is busy.. \n"));
+ goto out;
}
+
+
if (port->init_irqs) {
if (port->use_dma) {
- if (port == &ports[0]){
+ if (port == &ports[0]) {
#ifdef SYNC_SER_DMA
- if(request_irq(DMA4_INTR_VECT,
- tr_interrupt,
- 0,
- "synchronous serial 0 dma tr",
- &ports[0])) {
+ if (request_irq(DMA_OUT_INTR_VECT,
+ tr_interrupt,
+ 0,
+ "synchronous serial 0 dma tr",
+ &ports[0])) {
printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ");
- return -EBUSY;
- } else if(request_irq(DMA5_INTR_VECT,
- rx_interrupt,
- 0,
- "synchronous serial 1 dma rx",
- &ports[0])) {
- free_irq(DMA4_INTR_VECT, &port[0]);
+ goto out;
+ } else if (request_irq(DMA_IN_INTR_VECT,
+ rx_interrupt,
+ 0,
+ "synchronous serial 1 dma rx",
+ &ports[0])) {
+ free_irq(DMA_OUT_INTR_VECT, &port[0]);
printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ");
- return -EBUSY;
- } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR,
- "synchronous serial 0 dma tr",
- DMA_VERBOSE_ON_ERROR,
- 0,
- dma_sser0)) {
- free_irq(DMA4_INTR_VECT, &port[0]);
- free_irq(DMA5_INTR_VECT, &port[0]);
+ goto out;
+ } else if (crisv32_request_dma(OUT_DMA_NBR,
+ "synchronous serial 0 dma tr",
+ DMA_VERBOSE_ON_ERROR,
+ 0,
+ REQ_DMA_SYNCSER)) {
+ free_irq(DMA_OUT_INTR_VECT, &port[0]);
+ free_irq(DMA_IN_INTR_VECT, &port[0]);
printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel");
- return -EBUSY;
- } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR,
- "synchronous serial 0 dma rec",
- DMA_VERBOSE_ON_ERROR,
- 0,
- dma_sser0)) {
- crisv32_free_dma(SYNC_SER0_TX_DMA_NBR);
- free_irq(DMA4_INTR_VECT, &port[0]);
- free_irq(DMA5_INTR_VECT, &port[0]);
+ goto out;
+ } else if (crisv32_request_dma(IN_DMA_NBR,
+ "synchronous serial 0 dma rec",
+ DMA_VERBOSE_ON_ERROR,
+ 0,
+ REQ_DMA_SYNCSER)) {
+ crisv32_free_dma(OUT_DMA_NBR);
+ free_irq(DMA_OUT_INTR_VECT, &port[0]);
+ free_irq(DMA_IN_INTR_VECT, &port[0]);
printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel");
- return -EBUSY;
+ goto out;
}
#endif
}
- else if (port == &ports[1]){
+#ifdef CONFIG_ETRAXFS
+ else if (port == &ports[1]) {
#ifdef SYNC_SER_DMA
if (request_irq(DMA6_INTR_VECT,
tr_interrupt,
@@ -409,7 +505,7 @@ static int sync_serial_open(struct inode *inode, struct file *file)
"synchronous serial 1 dma tr",
&ports[1])) {
printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ");
- return -EBUSY;
+ goto out;
} else if (request_irq(DMA7_INTR_VECT,
rx_interrupt,
0,
@@ -417,37 +513,39 @@ static int sync_serial_open(struct inode *inode, struct file *file)
&ports[1])) {
free_irq(DMA6_INTR_VECT, &ports[1]);
printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ");
- return -EBUSY;
- } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR,
- "synchronous serial 1 dma tr",
- DMA_VERBOSE_ON_ERROR,
- 0,
- dma_sser1)) {
- free_irq(21, &ports[1]);
- free_irq(20, &ports[1]);
+ goto out;
+ } else if (crisv32_request_dma(
+ SYNC_SER1_TX_DMA_NBR,
+ "synchronous serial 1 dma tr",
+ DMA_VERBOSE_ON_ERROR,
+ 0,
+ dma_sser1)) {
+ free_irq(DMA6_INTR_VECT, &ports[1]);
+ free_irq(DMA7_INTR_VECT, &ports[1]);
printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel");
- return -EBUSY;
- } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR,
- "synchronous serial 3 dma rec",
- DMA_VERBOSE_ON_ERROR,
- 0,
- dma_sser1)) {
+ goto out;
+ } else if (crisv32_request_dma(
+ SYNC_SER1_RX_DMA_NBR,
+ "synchronous serial 3 dma rec",
+ DMA_VERBOSE_ON_ERROR,
+ 0,
+ dma_sser1)) {
crisv32_free_dma(SYNC_SER1_TX_DMA_NBR);
free_irq(DMA6_INTR_VECT, &ports[1]);
free_irq(DMA7_INTR_VECT, &ports[1]);
printk(KERN_CRIT "Can't allocate sync serial port 3 RX DMA channel");
- return -EBUSY;
+ goto out;
}
#endif
}
-
+#endif
/* Enable DMAs */
REG_WR(dma, port->regi_dmain, rw_cfg, cfg);
REG_WR(dma, port->regi_dmaout, rw_cfg, cfg);
/* Enable DMA IRQs */
REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask);
REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask);
- /* Set up wordsize = 2 for DMAs. */
+ /* Set up wordsize = 1 for DMAs. */
DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1);
DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1);
@@ -456,41 +554,48 @@ static int sync_serial_open(struct inode *inode, struct file *file)
} else { /* !port->use_dma */
#ifdef SYNC_SER_MANUAL
if (port == &ports[0]) {
- if (request_irq(SSER0_INTR_VECT,
+ if (request_irq(SYNCSER_INTR_VECT,
manual_interrupt,
0,
"synchronous serial manual irq",
&ports[0])) {
printk("Can't allocate sync serial manual irq");
- return -EBUSY;
+ goto out;
}
- } else if (port == &ports[1]) {
+ }
+#ifdef CONFIG_ETRAXFS
+ else if (port == &ports[1]) {
if (request_irq(SSER1_INTR_VECT,
manual_interrupt,
0,
"synchronous serial manual irq",
&ports[1])) {
printk(KERN_CRIT "Can't allocate sync serial manual irq");
- return -EBUSY;
+ goto out;
}
}
+#endif
port->init_irqs = 0;
#else
panic("sync_serial: Manual mode not supported.\n");
#endif /* SYNC_SER_MANUAL */
}
+
} /* port->init_irqs */
port->busy++;
- return 0;
+ ret = 0;
+out:
+ mutex_unlock(&sync_serial_mutex);
+ return ret;
}
static int sync_serial_release(struct inode *inode, struct file *file)
{
- int dev = MINOR(inode->i_rdev);
- sync_port* port;
+ int dev = iminor(inode);
+ sync_port *port;
- if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled)
{
DEBUG(printk("Invalid minor %d\n", dev));
return -ENODEV;
@@ -505,19 +610,39 @@ static int sync_serial_release(struct inode *inode, struct file *file)
static unsigned int sync_serial_poll(struct file *file, poll_table *wait)
{
- int dev = MINOR(file->f_dentry->d_inode->i_rdev);
+ int dev = iminor(file_inode(file));
unsigned int mask = 0;
- sync_port* port;
+ sync_port *port;
DEBUGPOLL( static unsigned int prev_mask = 0; );
port = &ports[dev];
+
+ if (!port->started) {
+ reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg);
+ reg_sser_rw_rec_cfg rec_cfg =
+ REG_RD(sser, port->regi_sser, rw_rec_cfg);
+ cfg.en = regk_sser_yes;
+ rec_cfg.rec_en = port->input;
+ REG_WR(sser, port->regi_sser, rw_cfg, cfg);
+ REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
+ port->started = 1;
+ }
+
poll_wait(file, &port->out_wait_q, wait);
poll_wait(file, &port->in_wait_q, wait);
- /* Some room to write */
- if (port->out_count < OUT_BUFFER_SIZE)
+
+ /* No active transfer, descriptors are available */
+ if (port->output && !port->tr_running)
+ mask |= POLLOUT | POLLWRNORM;
+
+ /* Descriptor and buffer space available. */
+ if (port->output &&
+ port->active_tr_descr != port->catch_tr_descr &&
+ port->out_buf_count < OUT_BUFFER_SIZE)
mask |= POLLOUT | POLLWRNORM;
+
/* At least an inbufchunk of data */
- if (sync_data_avail(port) >= port->inbufchunk)
+ if (port->input && sync_data_avail(port) >= port->inbufchunk)
mask |= POLLIN | POLLRDNORM;
DEBUGPOLL(if (mask != prev_mask)
@@ -528,19 +653,20 @@ static unsigned int sync_serial_poll(struct file *file, poll_table *wait)
return mask;
}
-static int sync_serial_ioctl(struct inode *inode, struct file *file,
+static int sync_serial_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int return_val = 0;
- int dev = MINOR(file->f_dentry->d_inode->i_rdev);
- sync_port* port;
+ int dma_w_size = regk_dma_set_w_size1;
+ int dev = iminor(file_inode(file));
+ sync_port *port;
reg_sser_rw_tr_cfg tr_cfg;
reg_sser_rw_rec_cfg rec_cfg;
reg_sser_rw_frm_cfg frm_cfg;
reg_sser_rw_cfg gen_cfg;
reg_sser_rw_intr_mask intr_mask;
- if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled)
{
DEBUG(printk("Invalid minor %d\n", dev));
return -1;
@@ -559,61 +685,81 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
case SSP_SPEED:
if (GET_SPEED(arg) == CODEC)
{
+ unsigned int freq;
+
gen_cfg.base_freq = regk_sser_f32;
- /* FREQ = 0 => 4 MHz => clk_div = 7*/
- gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg));
- }
- else
- {
+
+ /* Clock divider will internally be
+ * gen_cfg.clk_div + 1.
+ */
+
+ freq = GET_FREQ(arg);
+ switch (freq) {
+ case FREQ_32kHz:
+ case FREQ_64kHz:
+ case FREQ_128kHz:
+ case FREQ_256kHz:
+ gen_cfg.clk_div = 125 *
+ (1 << (freq - FREQ_256kHz)) - 1;
+ break;
+ case FREQ_512kHz:
+ gen_cfg.clk_div = 62;
+ break;
+ case FREQ_1MHz:
+ case FREQ_2MHz:
+ case FREQ_4MHz:
+ gen_cfg.clk_div = 8 * (1 << freq) - 1;
+ break;
+ }
+ } else {
gen_cfg.base_freq = regk_sser_f29_493;
- switch (GET_SPEED(arg))
- {
- case SSP150:
- gen_cfg.clk_div = 29493000 / (150 * 8) - 1;
- break;
- case SSP300:
- gen_cfg.clk_div = 29493000 / (300 * 8) - 1;
- break;
- case SSP600:
- gen_cfg.clk_div = 29493000 / (600 * 8) - 1;
- break;
- case SSP1200:
- gen_cfg.clk_div = 29493000 / (1200 * 8) - 1;
- break;
- case SSP2400:
- gen_cfg.clk_div = 29493000 / (2400 * 8) - 1;
- break;
- case SSP4800:
- gen_cfg.clk_div = 29493000 / (4800 * 8) - 1;
- break;
- case SSP9600:
- gen_cfg.clk_div = 29493000 / (9600 * 8) - 1;
- break;
- case SSP19200:
- gen_cfg.clk_div = 29493000 / (19200 * 8) - 1;
- break;
- case SSP28800:
- gen_cfg.clk_div = 29493000 / (28800 * 8) - 1;
- break;
- case SSP57600:
- gen_cfg.clk_div = 29493000 / (57600 * 8) - 1;
- break;
- case SSP115200:
- gen_cfg.clk_div = 29493000 / (115200 * 8) - 1;
- break;
- case SSP230400:
- gen_cfg.clk_div = 29493000 / (230400 * 8) - 1;
- break;
- case SSP460800:
- gen_cfg.clk_div = 29493000 / (460800 * 8) - 1;
- break;
- case SSP921600:
- gen_cfg.clk_div = 29493000 / (921600 * 8) - 1;
- break;
- case SSP3125000:
- gen_cfg.base_freq = regk_sser_f100;
- gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1;
- break;
+ switch (GET_SPEED(arg)) {
+ case SSP150:
+ gen_cfg.clk_div = 29493000 / (150 * 8) - 1;
+ break;
+ case SSP300:
+ gen_cfg.clk_div = 29493000 / (300 * 8) - 1;
+ break;
+ case SSP600:
+ gen_cfg.clk_div = 29493000 / (600 * 8) - 1;
+ break;
+ case SSP1200:
+ gen_cfg.clk_div = 29493000 / (1200 * 8) - 1;
+ break;
+ case SSP2400:
+ gen_cfg.clk_div = 29493000 / (2400 * 8) - 1;
+ break;
+ case SSP4800:
+ gen_cfg.clk_div = 29493000 / (4800 * 8) - 1;
+ break;
+ case SSP9600:
+ gen_cfg.clk_div = 29493000 / (9600 * 8) - 1;
+ break;
+ case SSP19200:
+ gen_cfg.clk_div = 29493000 / (19200 * 8) - 1;
+ break;
+ case SSP28800:
+ gen_cfg.clk_div = 29493000 / (28800 * 8) - 1;
+ break;
+ case SSP57600:
+ gen_cfg.clk_div = 29493000 / (57600 * 8) - 1;
+ break;
+ case SSP115200:
+ gen_cfg.clk_div = 29493000 / (115200 * 8) - 1;
+ break;
+ case SSP230400:
+ gen_cfg.clk_div = 29493000 / (230400 * 8) - 1;
+ break;
+ case SSP460800:
+ gen_cfg.clk_div = 29493000 / (460800 * 8) - 1;
+ break;
+ case SSP921600:
+ gen_cfg.clk_div = 29493000 / (921600 * 8) - 1;
+ break;
+ case SSP3125000:
+ gen_cfg.base_freq = regk_sser_f100;
+ gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1;
+ break;
}
}
@@ -626,46 +772,60 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
case MASTER_OUTPUT:
port->output = 1;
port->input = 0;
+ frm_cfg.out_on = regk_sser_tr;
+ frm_cfg.frame_pin_dir = regk_sser_out;
gen_cfg.clk_dir = regk_sser_out;
break;
case SLAVE_OUTPUT:
port->output = 1;
port->input = 0;
+ frm_cfg.frame_pin_dir = regk_sser_in;
gen_cfg.clk_dir = regk_sser_in;
break;
case MASTER_INPUT:
port->output = 0;
port->input = 1;
+ frm_cfg.frame_pin_dir = regk_sser_out;
+ frm_cfg.out_on = regk_sser_intern_tb;
gen_cfg.clk_dir = regk_sser_out;
break;
case SLAVE_INPUT:
port->output = 0;
port->input = 1;
+ frm_cfg.frame_pin_dir = regk_sser_in;
gen_cfg.clk_dir = regk_sser_in;
break;
case MASTER_BIDIR:
port->output = 1;
port->input = 1;
+ frm_cfg.frame_pin_dir = regk_sser_out;
+ frm_cfg.out_on = regk_sser_intern_tb;
gen_cfg.clk_dir = regk_sser_out;
break;
case SLAVE_BIDIR:
port->output = 1;
port->input = 1;
+ frm_cfg.frame_pin_dir = regk_sser_in;
gen_cfg.clk_dir = regk_sser_in;
break;
default:
spin_unlock_irq(&port->lock);
return -EINVAL;
-
}
if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT))
intr_mask.rdav = regk_sser_yes;
break;
case SSP_FRAME_SYNC:
- if (arg & NORMAL_SYNC)
+ if (arg & NORMAL_SYNC) {
+ frm_cfg.rec_delay = 1;
frm_cfg.tr_delay = 1;
+ }
else if (arg & EARLY_SYNC)
- frm_cfg.tr_delay = 0;
+ frm_cfg.rec_delay = frm_cfg.tr_delay = 0;
+ else if (arg & SECOND_WORD_SYNC) {
+ frm_cfg.rec_delay = 7;
+ frm_cfg.tr_delay = 1;
+ }
tr_cfg.bulk_wspace = frm_cfg.tr_delay;
frm_cfg.early_wend = regk_sser_yes;
@@ -681,9 +841,11 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
else if (arg & SYNC_OFF)
frm_cfg.frame_pin_use = regk_sser_gio0;
- if (arg & WORD_SIZE_8)
+ dma_w_size = regk_dma_set_w_size2;
+ if (arg & WORD_SIZE_8) {
rec_cfg.sample_size = tr_cfg.sample_size = 7;
- else if (arg & WORD_SIZE_12)
+ dma_w_size = regk_dma_set_w_size1;
+ } else if (arg & WORD_SIZE_12)
rec_cfg.sample_size = tr_cfg.sample_size = 11;
else if (arg & WORD_SIZE_16)
rec_cfg.sample_size = tr_cfg.sample_size = 15;
@@ -697,10 +859,13 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
else if (arg & BIT_ORDER_LSB)
rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst;
- if (arg & FLOW_CONTROL_ENABLE)
+ if (arg & FLOW_CONTROL_ENABLE) {
+ frm_cfg.status_pin_use = regk_sser_frm;
rec_cfg.fifo_thr = regk_sser_thr16;
- else if (arg & FLOW_CONTROL_DISABLE)
+ } else if (arg & FLOW_CONTROL_DISABLE) {
+ frm_cfg.status_pin_use = regk_sser_gio0;
rec_cfg.fifo_thr = regk_sser_inf;
+ }
if (arg & CLOCK_NOT_GATED)
gen_cfg.gate_clk = regk_sser_no;
@@ -727,9 +892,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
break;
case SSP_OPOLARITY:
if (arg & CLOCK_NORMAL)
- gen_cfg.out_clk_pol = regk_sser_neg;
- else if (arg & CLOCK_INVERT)
gen_cfg.out_clk_pol = regk_sser_pos;
+ else if (arg & CLOCK_INVERT)
+ gen_cfg.out_clk_pol = regk_sser_neg;
if (arg & FRAME_NORMAL)
frm_cfg.level = regk_sser_pos_hi;
@@ -771,10 +936,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
}
- if (port->started)
- {
- tr_cfg.tr_en = port->output;
+ if (port->started) {
rec_cfg.rec_en = port->input;
+ gen_cfg.en = (port->output | port->input);
}
REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
@@ -783,151 +947,170 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file,
REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask);
REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
+
+ if (cmd == SSP_FRAME_SYNC && (arg & (WORD_SIZE_8 | WORD_SIZE_12 |
+ WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) {
+ int en = gen_cfg.en;
+ gen_cfg.en = 0;
+ REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
+ /* ##### Should DMA be stoped before we change dma size? */
+ DMA_WR_CMD(port->regi_dmain, dma_w_size);
+ DMA_WR_CMD(port->regi_dmaout, dma_w_size);
+ gen_cfg.en = en;
+ REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
+ }
+
spin_unlock_irq(&port->lock);
return return_val;
}
-static ssize_t sync_serial_write(struct file * file, const char * buf,
- size_t count, loff_t *ppos)
+static long sync_serial_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ mutex_lock(&sync_serial_mutex);
+ ret = sync_serial_ioctl_unlocked(file, cmd, arg);
+ mutex_unlock(&sync_serial_mutex);
+
+ return ret;
+}
+
+/* NOTE: sync_serial_write does not support concurrency */
+static ssize_t sync_serial_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
{
- int dev = MINOR(file->f_dentry->d_inode->i_rdev);
+ int dev = iminor(file_inode(file));
DECLARE_WAITQUEUE(wait, current);
- sync_port *port;
- unsigned long c, c1;
- unsigned long free_outp;
- unsigned long outp;
- unsigned long out_buffer;
+ struct sync_port *port;
+ int trunc_count;
unsigned long flags;
+ int bytes_free;
+ int out_buf_count;
- if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
- {
+ unsigned char *rd_ptr; /* First allocated byte in the buffer */
+ unsigned char *wr_ptr; /* First free byte in the buffer */
+ unsigned char *buf_stop_ptr; /* Last byte + 1 */
+
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) {
DEBUG(printk("Invalid minor %d\n", dev));
return -ENODEV;
}
port = &ports[dev];
- DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE));
- /* Space to end of buffer */
- /*
- * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE
- * outp^ +out_count
- ^free_outp
- * out_buffer 45<- c ->0123OUT_BUFFER_SIZE
- * +out_count outp^
- * free_outp
- *
+ /* |<- OUT_BUFFER_SIZE ->|
+ * |<- out_buf_count ->|
+ * |<- trunc_count ->| ...->|
+ * ______________________________________________________
+ * | free | data | free |
+ * |_________|___________________|________________________|
+ * ^ rd_ptr ^ wr_ptr
*/
+ DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu a: %p c: %p\n",
+ port->port_nbr, count, port->active_tr_descr,
+ port->catch_tr_descr));
/* Read variables that may be updated by interrupts */
spin_lock_irqsave(&port->lock, flags);
- count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count;
- outp = (unsigned long)port->outp;
- free_outp = outp + port->out_count;
+ rd_ptr = port->out_rd_ptr;
+ out_buf_count = port->out_buf_count;
spin_unlock_irqrestore(&port->lock, flags);
- out_buffer = (unsigned long)port->out_buffer;
- /* Find out where and how much to write */
- if (free_outp >= out_buffer + OUT_BUFFER_SIZE)
- free_outp -= OUT_BUFFER_SIZE;
- if (free_outp >= outp)
- c = out_buffer + OUT_BUFFER_SIZE - free_outp;
- else
- c = outp - free_outp;
- if (c > count)
- c = count;
+ /* Check if resources are available */
+ if (port->tr_running &&
+ ((port->use_dma && port->active_tr_descr == port->catch_tr_descr) ||
+ out_buf_count >= OUT_BUFFER_SIZE)) {
+ DEBUGWRITE(printk(KERN_DEBUG "sser%d full\n", dev));
+ return -EAGAIN;
+ }
+
+ buf_stop_ptr = port->out_buffer + OUT_BUFFER_SIZE;
+
+ /* Determine pointer to the first free byte, before copying. */
+ wr_ptr = rd_ptr + out_buf_count;
+ if (wr_ptr >= buf_stop_ptr)
+ wr_ptr -= OUT_BUFFER_SIZE;
+
+ /* If we wrap the ring buffer, let the user space program handle it by
+ * truncating the data. This could be more elegant, small buffer
+ * fragments may occur.
+ */
+ bytes_free = OUT_BUFFER_SIZE - out_buf_count;
+ if (wr_ptr + bytes_free > buf_stop_ptr)
+ bytes_free = buf_stop_ptr - wr_ptr;
+ trunc_count = (count < bytes_free) ? count : bytes_free;
-// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c));
- if (copy_from_user((void*)free_outp, buf, c))
+ if (copy_from_user(wr_ptr, buf, trunc_count))
return -EFAULT;
- if (c != count) {
- buf += c;
- c1 = count - c;
- DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1));
- if (copy_from_user((void*)out_buffer, buf, c1))
- return -EFAULT;
- }
- spin_lock_irqsave(&port->lock, flags);
- port->out_count += count;
- spin_unlock_irqrestore(&port->lock, flags);
+ DEBUGOUTBUF(printk(KERN_DEBUG "%-4d + %-4d = %-4d %p %p %p\n",
+ out_buf_count, trunc_count,
+ port->out_buf_count, port->out_buffer,
+ wr_ptr, buf_stop_ptr));
/* Make sure transmitter/receiver is running */
- if (!port->started)
- {
+ if (!port->started) {
reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg);
- reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg);
reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg);
cfg.en = regk_sser_yes;
- tr_cfg.tr_en = port->output;
rec_cfg.rec_en = port->input;
REG_WR(sser, port->regi_sser, rw_cfg, cfg);
- REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
port->started = 1;
}
- if (file->f_flags & O_NONBLOCK) {
- spin_lock_irqsave(&port->lock, flags);
- if (!port->tr_running) {
- if (!port->use_dma) {
- reg_sser_rw_intr_mask intr_mask;
- intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask);
- /* Start sender by writing data */
- send_word(port);
- /* and enable transmitter ready IRQ */
- intr_mask.trdy = 1;
- REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask);
- } else {
- start_dma(port, (unsigned char* volatile )port->outp, c);
- }
- }
- spin_unlock_irqrestore(&port->lock, flags);
- DEBUGWRITE(printk("w d%d c %lu NB\n",
- port->port_nbr, count));
- return count;
+ /* Setup wait if blocking */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ add_wait_queue(&port->out_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
}
- /* Sleep until all sent */
-
- add_wait_queue(&port->out_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&port->lock, flags);
- if (!port->tr_running) {
- if (!port->use_dma) {
- reg_sser_rw_intr_mask intr_mask;
- intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask);
- /* Start sender by writing data */
- send_word(port);
- /* and enable transmitter ready IRQ */
- intr_mask.trdy = 1;
- REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask);
- } else {
- start_dma(port, port->outp, c);
- }
+ port->out_buf_count += trunc_count;
+ if (port->use_dma) {
+ start_dma_out(port, wr_ptr, trunc_count);
+ } else if (!port->tr_running) {
+ reg_sser_rw_intr_mask intr_mask;
+ intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask);
+ /* Start sender by writing data */
+ send_word(port);
+ /* and enable transmitter ready IRQ */
+ intr_mask.trdy = 1;
+ REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask);
}
spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Exit if non blocking */
+ if (file->f_flags & O_NONBLOCK) {
+ DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu %08x\n",
+ port->port_nbr, trunc_count,
+ REG_RD_INT(dma, port->regi_dmaout, r_intr)));
+ return trunc_count;
+ }
+
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->out_wait_q, &wait);
+
if (signal_pending(current))
- {
return -EINTR;
- }
- DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count));
- return count;
+
+ DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n",
+ port->port_nbr, trunc_count));
+ return trunc_count;
}
static ssize_t sync_serial_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
- int dev = MINOR(file->f_dentry->d_inode->i_rdev);
+ int dev = iminor(file_inode(file));
int avail;
sync_port *port;
unsigned char* start;
unsigned char* end;
unsigned long flags;
- if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled)
{
DEBUG(printk("Invalid minor %d\n", dev));
return -ENODEV;
@@ -950,7 +1133,6 @@ static ssize_t sync_serial_read(struct file * file, char * buf,
port->started = 1;
}
-
/* Calculate number of available bytes */
/* Save pointers to avoid that they are modified by interrupt */
spin_lock_irqsave(&port->lock, flags);
@@ -959,16 +1141,15 @@ static ssize_t sync_serial_read(struct file * file, char * buf,
spin_unlock_irqrestore(&port->lock, flags);
while ((start == end) && !port->full) /* No data */
{
+ DEBUGREAD(printk(KERN_DEBUG "&"));
if (file->f_flags & O_NONBLOCK)
- {
return -EAGAIN;
- }
- interruptible_sleep_on(&port->in_wait_q);
+ wait_event_interruptible(port->in_wait_q,
+ !(start == end && !port->full));
if (signal_pending(current))
- {
return -EINTR;
- }
+
spin_lock_irqsave(&port->lock, flags);
start = (unsigned char*)port->readp; /* cast away volatile */
end = (unsigned char*)port->writep; /* cast away volatile */
@@ -1005,83 +1186,105 @@ static void send_word(sync_port* port)
switch(tr_cfg.sample_size)
{
case 8:
- port->out_count--;
- tr_data.data = *port->outp++;
+ port->out_buf_count--;
+ tr_data.data = *port->out_rd_ptr++;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
+ if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
break;
case 12:
{
- int data = (*port->outp++) << 8;
- data |= *port->outp++;
- port->out_count-=2;
+ int data = (*port->out_rd_ptr++) << 8;
+ data |= *port->out_rd_ptr++;
+ port->out_buf_count -= 2;
tr_data.data = data;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
+ if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
}
break;
case 16:
- port->out_count-=2;
- tr_data.data = *(unsigned short *)port->outp;
+ port->out_buf_count -= 2;
+ tr_data.data = *(unsigned short *)port->out_rd_ptr;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- port->outp+=2;
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
+ port->out_rd_ptr += 2;
+ if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
break;
case 24:
- port->out_count-=3;
- tr_data.data = *(unsigned short *)port->outp;
+ port->out_buf_count -= 3;
+ tr_data.data = *(unsigned short *)port->out_rd_ptr;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- port->outp+=2;
- tr_data.data = *port->outp++;
+ port->out_rd_ptr += 2;
+ tr_data.data = *port->out_rd_ptr++;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
+ if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
break;
case 32:
- port->out_count-=4;
- tr_data.data = *(unsigned short *)port->outp;
+ port->out_buf_count -= 4;
+ tr_data.data = *(unsigned short *)port->out_rd_ptr;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- port->outp+=2;
- tr_data.data = *(unsigned short *)port->outp;
+ port->out_rd_ptr += 2;
+ tr_data.data = *(unsigned short *)port->out_rd_ptr;
REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
- port->outp+=2;
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
+ port->out_rd_ptr += 2;
+ if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
break;
}
}
-
-static void start_dma(struct sync_port* port, const char* data, int count)
+static void start_dma_out(struct sync_port *port,
+ const char *data, int count)
{
- port->tr_running = 1;
- port->out_descr.buf = (char*)virt_to_phys((char*)data);
- port->out_descr.after = port->out_descr.buf + count;
- port->out_descr.eol = port->out_descr.intr = 1;
+ port->active_tr_descr->buf = (char *) virt_to_phys((char *) data);
+ port->active_tr_descr->after = port->active_tr_descr->buf + count;
+ port->active_tr_descr->intr = 1;
- port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr);
- port->out_context.saved_data_buf = port->out_descr.buf;
+ port->active_tr_descr->eol = 1;
+ port->prev_tr_descr->eol = 0;
+
+ DEBUGTRDMA(printk(KERN_DEBUG "Inserting eolr:%p eol@:%p\n",
+ port->prev_tr_descr, port->active_tr_descr));
+ port->prev_tr_descr = port->active_tr_descr;
+ port->active_tr_descr = phys_to_virt((int) port->active_tr_descr->next);
+
+ if (!port->tr_running) {
+ reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser,
+ rw_tr_cfg);
+
+ port->out_context.next = 0;
+ port->out_context.saved_data =
+ (dma_descr_data *)virt_to_phys(port->prev_tr_descr);
+ port->out_context.saved_data_buf = port->prev_tr_descr->buf;
+
+ DMA_START_CONTEXT(port->regi_dmaout,
+ virt_to_phys((char *)&port->out_context));
+
+ tr_cfg.tr_en = regk_sser_yes;
+ REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
+ DEBUGTRDMA(printk(KERN_DEBUG "dma s\n"););
+ } else {
+ DMA_CONTINUE_DATA(port->regi_dmaout);
+ DEBUGTRDMA(printk(KERN_DEBUG "dma c\n"););
+ }
- DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context));
- DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count));
+ port->tr_running = 1;
}
-static void start_dma_in(sync_port* port)
+static void start_dma_in(sync_port *port)
{
int i;
- char* buf;
+ char *buf;
port->writep = port->flip;
- if (port->writep > port->flip + port->in_buffer_size)
- {
+ if (port->writep > port->flip + port->in_buffer_size) {
panic("Offset too large in sync serial driver\n");
return;
}
buf = (char*)virt_to_phys(port->in_buffer);
- for (i = 0; i < NUM_IN_DESCR; i++) {
+ for (i = 0; i < NBR_IN_DESCR; i++) {
port->in_descr[i].buf = buf;
port->in_descr[i].after = buf + port->inbufchunk;
port->in_descr[i].intr = 1;
@@ -1093,59 +1296,126 @@ static void start_dma_in(sync_port* port)
port->in_descr[i-1].next = (dma_descr_data*)virt_to_phys(&port->in_descr[0]);
port->in_descr[i-1].eol = regk_sser_yes;
port->next_rx_desc = &port->in_descr[0];
- port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1];
+ port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR - 1];
port->in_context.saved_data = (dma_descr_data*)virt_to_phys(&port->in_descr[0]);
port->in_context.saved_data_buf = port->in_descr[0].buf;
DMA_START_CONTEXT(port->regi_dmain, virt_to_phys(&port->in_context));
}
#ifdef SYNC_SER_DMA
-static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t tr_interrupt(int irq, void *dev_id)
{
reg_dma_r_masked_intr masked;
reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes};
+ reg_dma_rw_stat stat;
int i;
- struct dma_descr_data *descr;
- unsigned int sentl;
int found = 0;
+ int stop_sser = 0;
- for (i = 0; i < NUMBER_OF_PORTS; i++)
- {
+ for (i = 0; i < NBR_PORTS; i++) {
sync_port *port = &ports[i];
- if (!port->enabled || !port->use_dma )
+ if (!port->enabled || !port->use_dma)
continue;
+ /* IRQ active for the port? */
masked = REG_RD(dma, port->regi_dmaout, r_masked_intr);
+ if (!masked.data)
+ continue;
- if (masked.data) /* IRQ active for the port? */
- {
- found = 1;
- /* Clear IRQ */
- REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr);
- descr = &port->out_descr;
- sentl = descr->after - descr->buf;
- port->out_count -= sentl;
- port->outp += sentl;
- if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
- port->outp = port->out_buffer;
- if (port->out_count) {
- int c;
- c = port->out_buffer + OUT_BUFFER_SIZE - port->outp;
- if (c > port->out_count)
- c = port->out_count;
- DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c));
- start_dma(port, port->outp, c);
- } else {
- DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl));
- port->tr_running = 0;
+ found = 1;
+
+ /* Check if we should stop the DMA transfer */
+ stat = REG_RD(dma, port->regi_dmaout, rw_stat);
+ if (stat.list_state == regk_dma_data_at_eol)
+ stop_sser = 1;
+
+ /* Clear IRQ */
+ REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr);
+
+ if (!stop_sser) {
+ /* The DMA has completed a descriptor, EOL was not
+ * encountered, so step relevant descriptor and
+ * datapointers forward. */
+ int sent;
+ sent = port->catch_tr_descr->after -
+ port->catch_tr_descr->buf;
+ DEBUGTXINT(printk(KERN_DEBUG "%-4d - %-4d = %-4d\t"
+ "in descr %p (ac: %p)\n",
+ port->out_buf_count, sent,
+ port->out_buf_count - sent,
+ port->catch_tr_descr,
+ port->active_tr_descr););
+ port->out_buf_count -= sent;
+ port->catch_tr_descr =
+ phys_to_virt((int) port->catch_tr_descr->next);
+ port->out_rd_ptr =
+ phys_to_virt((int) port->catch_tr_descr->buf);
+ } else {
+ int i, sent;
+ /* EOL handler.
+ * Note that if an EOL was encountered during the irq
+ * locked section of sync_ser_write the DMA will be
+ * restarted and the eol flag will be cleared.
+ * The remaining descriptors will be traversed by
+ * the descriptor interrupts as usual.
+ */
+ i = 0;
+ while (!port->catch_tr_descr->eol) {
+ sent = port->catch_tr_descr->after -
+ port->catch_tr_descr->buf;
+ DEBUGOUTBUF(printk(KERN_DEBUG
+ "traversing descr %p -%d (%d)\n",
+ port->catch_tr_descr,
+ sent,
+ port->out_buf_count));
+ port->out_buf_count -= sent;
+ port->catch_tr_descr = phys_to_virt(
+ (int)port->catch_tr_descr->next);
+ i++;
+ if (i >= NBR_OUT_DESCR) {
+ /* TODO: Reset and recover */
+ panic("sync_serial: missing eol");
+ }
}
- wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */
+ sent = port->catch_tr_descr->after -
+ port->catch_tr_descr->buf;
+ DEBUGOUTBUF(printk(KERN_DEBUG
+ "eol at descr %p -%d (%d)\n",
+ port->catch_tr_descr,
+ sent,
+ port->out_buf_count));
+
+ port->out_buf_count -= sent;
+
+ /* Update read pointer to first free byte, we
+ * may already be writing data there. */
+ port->out_rd_ptr =
+ phys_to_virt((int) port->catch_tr_descr->after);
+ if (port->out_rd_ptr > port->out_buffer +
+ OUT_BUFFER_SIZE)
+ port->out_rd_ptr = port->out_buffer;
+
+ reg_sser_rw_tr_cfg tr_cfg =
+ REG_RD(sser, port->regi_sser, rw_tr_cfg);
+ DEBUGTXINT(printk(KERN_DEBUG
+ "tr_int DMA stop %d, set catch @ %p\n",
+ port->out_buf_count,
+ port->active_tr_descr));
+ if (port->out_buf_count != 0)
+ printk(KERN_CRIT "sync_ser: buffer not "
+ "empty after eol.\n");
+ port->catch_tr_descr = port->active_tr_descr;
+ port->tr_running = 0;
+ tr_cfg.tr_en = regk_sser_no;
+ REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
}
+ /* wake up the waiting process */
+ wake_up_interruptible(&port->out_wait_q);
}
return IRQ_RETVAL(found);
} /* tr_interrupt */
-static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t rx_interrupt(int irq, void *dev_id)
{
reg_dma_r_masked_intr masked;
reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes};
@@ -1153,7 +1423,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
int i;
int found = 0;
- for (i = 0; i < NUMBER_OF_PORTS; i++)
+ for (i = 0; i < NBR_PORTS; i++)
{
sync_port *port = &ports[i];
@@ -1167,7 +1437,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
found = 1;
while (REG_RD(dma, port->regi_dmain, rw_data) !=
virt_to_phys(port->next_rx_desc)) {
-
+ DEBUGRXINT(printk(KERN_DEBUG "!"));
if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) {
int first_size = port->flip + port->in_buffer_size - port->writep;
memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size);
@@ -1186,11 +1456,16 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
port->full = 1;
}
- port->next_rx_desc->eol = 0;
- port->prev_rx_desc->eol = 1;
- port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc);
+ port->next_rx_desc->eol = 1;
+ port->prev_rx_desc->eol = 0;
+ /* Cache bug workaround */
+ flush_dma_descr(port->prev_rx_desc, 0);
+ port->prev_rx_desc = port->next_rx_desc;
port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next);
- wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */
+ /* Cache bug workaround */
+ flush_dma_descr(port->prev_rx_desc, 1);
+ /* wake up the waiting process */
+ wake_up_interruptible(&port->in_wait_q);
DMA_CONTINUE(port->regi_dmain);
REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr);
@@ -1202,15 +1477,15 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
#endif /* SYNC_SER_DMA */
#ifdef SYNC_SER_MANUAL
-static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t manual_interrupt(int irq, void *dev_id)
{
int i;
int found = 0;
reg_sser_r_masked_intr masked;
- for (i = 0; i < NUMBER_OF_PORTS; i++)
+ for (i = 0; i < NBR_PORTS; i++)
{
- sync_port* port = &ports[i];
+ sync_port *port = &ports[i];
if (!port->enabled || port->use_dma)
{
@@ -1264,7 +1539,7 @@ static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs
if (masked.trdy) /* Transmitter ready? */
{
found = 1;
- if (port->out_count > 0) /* More data to send */
+ if (port->out_buf_count > 0) /* More data to send */
send_word(port);
else /* transmission finished */
{