mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
560
arch/cris/arch-v10/drivers/Kconfig
Normal file
560
arch/cris/arch-v10/drivers/Kconfig
Normal file
|
@ -0,0 +1,560 @@
|
|||
if ETRAX_ARCH_V10
|
||||
|
||||
config ETRAX_ETHERNET
|
||||
bool "Ethernet support"
|
||||
depends on ETRAX_ARCH_V10 && NETDEVICES
|
||||
select MII
|
||||
help
|
||||
This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
|
||||
controller.
|
||||
|
||||
config ETRAX_SERIAL
|
||||
bool "Serial-port support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
help
|
||||
Enables the ETRAX 100 serial driver for ser0 (ttyS0)
|
||||
You probably want this enabled.
|
||||
|
||||
config ETRAX_SERIAL_FAST_TIMER
|
||||
bool "Use fast timers for serial DMA flush (experimental)"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Select this to have the serial DMAs flushed at a higher rate than
|
||||
normally, possible by using the fast timer API, the timeout is
|
||||
approx. 4 character times.
|
||||
If unsure, say N.
|
||||
|
||||
config ETRAX_SERIAL_FLUSH_DMA_FAST
|
||||
bool "Fast serial port DMA flush"
|
||||
depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER
|
||||
help
|
||||
Select this to have the serial DMAs flushed at a higher rate than
|
||||
normally possible through a fast timer interrupt (currently at
|
||||
15360 Hz).
|
||||
If unsure, say N.
|
||||
|
||||
config ETRAX_SERIAL_RX_TIMEOUT_TICKS
|
||||
int "Receive flush timeout (ticks) "
|
||||
depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER && !ETRAX_SERIAL_FLUSH_DMA_FAST
|
||||
default "5"
|
||||
help
|
||||
Number of timer ticks between flush of receive fifo (1 tick = 10ms).
|
||||
Try 0-3 for low latency applications. Approx 5 for high load
|
||||
applications (e.g. PPP). Maybe this should be more adaptive some
|
||||
day...
|
||||
|
||||
config ETRAX_SERIAL_PORT0
|
||||
bool "Serial port 0 enabled"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Enables the ETRAX 100 serial driver for ser0 (ttyS0)
|
||||
Normally you want this on, unless you use external DMA 1 that uses
|
||||
the same DMA channels.
|
||||
|
||||
choice
|
||||
prompt "Ser0 DTR, RI, DSR and CD assignment"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE
|
||||
|
||||
config ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE
|
||||
bool "No_DTR_RI_DSR_CD"
|
||||
|
||||
config ETRAX_SER0_DTR_RI_DSR_CD_ON_PA
|
||||
bool "DTR_RI_DSR_CD_on_PA"
|
||||
|
||||
config ETRAX_SER0_DTR_RI_DSR_CD_ON_PB
|
||||
bool "DTR_RI_DSR_CD_on_PB"
|
||||
help
|
||||
Enables the status and control signals DTR, RI, DSR and CD on PB for
|
||||
ser0.
|
||||
|
||||
config ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER0_DTR_ON_PA_BIT
|
||||
int "Ser0 DTR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER0_RI_ON_PA_BIT
|
||||
int "Ser0 RI on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER0_DSR_ON_PA_BIT
|
||||
int "Ser0 DSR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER0_CD_ON_PA_BIT
|
||||
int "Ser0 CD on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER0_DTR_ON_PB_BIT
|
||||
int "Ser0 DTR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the DTR signal for serial
|
||||
port 0.
|
||||
|
||||
config ETRAX_SER0_RI_ON_PB_BIT
|
||||
int "Ser0 RI on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the RI signal for serial
|
||||
port 0.
|
||||
|
||||
config ETRAX_SER0_DSR_ON_PB_BIT
|
||||
int "Ser0 DSR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the DSR signal for serial
|
||||
port 0.
|
||||
|
||||
config ETRAX_SER0_CD_ON_PB_BIT
|
||||
int "Ser0 CD on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the CD signal for serial
|
||||
port 0.
|
||||
|
||||
config ETRAX_SERIAL_PORT1
|
||||
bool "Serial port 1 enabled"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Enables the ETRAX 100 serial driver for ser1 (ttyS1).
|
||||
|
||||
choice
|
||||
prompt "Ser1 DTR, RI, DSR and CD assignment"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE
|
||||
|
||||
config ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE
|
||||
bool "No_DTR_RI_DSR_CD"
|
||||
|
||||
config ETRAX_SER1_DTR_RI_DSR_CD_ON_PA
|
||||
bool "DTR_RI_DSR_CD_on_PA"
|
||||
|
||||
config ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
|
||||
bool "DTR_RI_DSR_CD_on_PB"
|
||||
help
|
||||
Enables the status and control signals DTR, RI, DSR and CD on PB for
|
||||
ser1.
|
||||
|
||||
config ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER1_DTR_ON_PA_BIT
|
||||
int "Ser1 DTR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER1_RI_ON_PA_BIT
|
||||
int "Ser1 RI on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER1_DSR_ON_PA_BIT
|
||||
int "Ser1 DSR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER1_CD_ON_PA_BIT
|
||||
int "Ser1 CD on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER1_DTR_ON_PB_BIT
|
||||
int "Ser1 DTR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the DTR signal for serial
|
||||
port 1.
|
||||
|
||||
config ETRAX_SER1_RI_ON_PB_BIT
|
||||
int "Ser1 RI on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the RI signal for serial
|
||||
port 1.
|
||||
|
||||
config ETRAX_SER1_DSR_ON_PB_BIT
|
||||
int "Ser1 DSR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the DSR signal for serial
|
||||
port 1.
|
||||
|
||||
config ETRAX_SER1_CD_ON_PB_BIT
|
||||
int "Ser1 CD on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PB port to carry the CD signal for serial
|
||||
port 1.
|
||||
|
||||
comment "Make sure you do not have the same PB bits more than once!"
|
||||
depends on ETRAX_SERIAL && ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
|
||||
|
||||
config ETRAX_SERIAL_PORT2
|
||||
bool "Serial port 2 enabled"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Enables the ETRAX 100 serial driver for ser2 (ttyS2).
|
||||
|
||||
choice
|
||||
prompt "Ser2 DTR, RI, DSR and CD assignment"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE
|
||||
|
||||
config ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE
|
||||
bool "No_DTR_RI_DSR_CD"
|
||||
|
||||
config ETRAX_SER2_DTR_RI_DSR_CD_ON_PA
|
||||
bool "DTR_RI_DSR_CD_on_PA"
|
||||
help
|
||||
Enables the status and control signals DTR, RI, DSR and CD on PA for
|
||||
ser2.
|
||||
|
||||
config ETRAX_SER2_DTR_RI_DSR_CD_ON_PB
|
||||
bool "DTR_RI_DSR_CD_on_PB"
|
||||
|
||||
config ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER2_DTR_ON_PA_BIT
|
||||
int "Ser2 DTR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PA port to carry the DTR signal for serial
|
||||
port 2.
|
||||
|
||||
config ETRAX_SER2_RI_ON_PA_BIT
|
||||
int "Ser2 RI on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PA port to carry the RI signal for serial
|
||||
port 2.
|
||||
|
||||
config ETRAX_SER2_DSR_ON_PA_BIT
|
||||
int "Ser2 DSR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PA port to carry the DTR signal for serial
|
||||
port 2.
|
||||
|
||||
config ETRAX_SER2_CD_ON_PA_BIT
|
||||
int "Ser2 CD on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
help
|
||||
Specify the pin of the PA port to carry the CD signal for serial
|
||||
port 2.
|
||||
|
||||
config ETRAX_SER2_DTR_ON_PB_BIT
|
||||
int "Ser2 DTR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER2_RI_ON_PB_BIT
|
||||
int "Ser2 RI on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER2_DSR_ON_PB_BIT
|
||||
int "Ser2 DSR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SER2_CD_ON_PB_BIT
|
||||
int "Ser2 CD on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
|
||||
|
||||
config ETRAX_SERIAL_PORT3
|
||||
bool "Serial port 3 enabled"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Enables the ETRAX 100 serial driver for ser3 (ttyS3).
|
||||
|
||||
choice
|
||||
prompt "Ser3 DTR, RI, DSR and CD assignment"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE
|
||||
|
||||
config ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE
|
||||
bool "No_DTR_RI_DSR_CD"
|
||||
|
||||
config ETRAX_SER3_DTR_RI_DSR_CD_ON_PA
|
||||
bool "DTR_RI_DSR_CD_on_PA"
|
||||
|
||||
config ETRAX_SER3_DTR_RI_DSR_CD_ON_PB
|
||||
bool "DTR_RI_DSR_CD_on_PB"
|
||||
|
||||
config ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER3_DTR_ON_PA_BIT
|
||||
int "Ser3 DTR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_RI_ON_PA_BIT
|
||||
int "Ser3 RI on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_DSR_ON_PA_BIT
|
||||
int "Ser3 DSR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_CD_ON_PA_BIT
|
||||
int "Ser3 CD on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_DTR_ON_PB_BIT
|
||||
int "Ser3 DTR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_RI_ON_PB_BIT
|
||||
int "Ser3 RI on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_DSR_ON_PB_BIT
|
||||
int "Ser3 DSR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
config ETRAX_SER3_CD_ON_PB_BIT
|
||||
int "Ser3 CD on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default "-1"
|
||||
|
||||
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://en.wikipedia.org/wiki/Rs485>
|
||||
|
||||
config ETRAX_RS485_ON_PA
|
||||
bool "RS-485 mode on PA"
|
||||
depends on ETRAX_RS485
|
||||
help
|
||||
Control Driver Output Enable on RS485 transceiver using a pin on PA
|
||||
port:
|
||||
Axis 2400/2401 uses PA 3.
|
||||
|
||||
config ETRAX_RS485_ON_PA_BIT
|
||||
int "RS-485 mode on PA bit"
|
||||
depends on ETRAX_RS485_ON_PA
|
||||
default "3"
|
||||
help
|
||||
Control Driver Output Enable on RS485 transceiver using a this bit
|
||||
on PA port.
|
||||
|
||||
config ETRAX_RS485_DISABLE_RECEIVER
|
||||
bool "Disable serial receiver"
|
||||
depends on ETRAX_RS485
|
||||
help
|
||||
It's 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_USB_HOST
|
||||
bool "USB host"
|
||||
select USB
|
||||
help
|
||||
This option enables the host functionality of the ETRAX 100LX
|
||||
built-in USB controller. In host mode the controller is designed
|
||||
for CTRL and BULK traffic only, INTR traffic may work as well
|
||||
however (depending on the requirements of timeliness).
|
||||
|
||||
config ETRAX_PTABLE_SECTOR
|
||||
int "Byte-offset of partition table sector"
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default "65536"
|
||||
help
|
||||
Byte-offset of the partition table in the first flash chip.
|
||||
The default value is 64kB and should not be changed unless
|
||||
you know exactly what you are doing. The only valid reason
|
||||
for changing this is when the flash block size is bigger
|
||||
than 64kB (e.g. when using two parallel 16 bit flashes).
|
||||
|
||||
config ETRAX_I2C
|
||||
bool "I2C support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
help
|
||||
Enables an I2C driver on ETRAX100.
|
||||
EXAMPLE usage:
|
||||
i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
|
||||
ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);
|
||||
i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
|
||||
val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
|
||||
|
||||
# this is true for most products since PB-I2C seems to be somewhat
|
||||
# flawed..
|
||||
config ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
bool "I2C uses PB not PB-I2C"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
Select whether to use the special I2C mode in the PB I/O register or
|
||||
not. This option needs to be selected in order to use some drivers
|
||||
that access the I2C I/O pins directly instead of going through the
|
||||
I2C driver, like the DS1302 realtime-clock driver. If you are
|
||||
uncertain, choose Y here.
|
||||
|
||||
config ETRAX_I2C_DATA_PORT
|
||||
int "I2C SDA bit number"
|
||||
depends on ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
default "0"
|
||||
help
|
||||
Selects the pin on Port B where the data pin is connected
|
||||
|
||||
config ETRAX_I2C_CLK_PORT
|
||||
int "I2C SCL bit number"
|
||||
depends on ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
default "1"
|
||||
help
|
||||
Select the pin on Port B where the clock pin is connected
|
||||
|
||||
config ETRAX_I2C_EEPROM
|
||||
bool "I2C EEPROM (non-volatile RAM) support"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C
|
||||
driver. Select size option: Probed, 2k, 8k, 16k.
|
||||
(Probing works for 2k and 8k but not that well for 16k)
|
||||
|
||||
choice
|
||||
prompt "EEPROM size"
|
||||
depends on ETRAX_I2C_EEPROM
|
||||
default ETRAX_I2C_EEPROM_PROBE
|
||||
|
||||
config ETRAX_I2C_EEPROM_PROBE
|
||||
bool "Probed"
|
||||
help
|
||||
Specifies size or auto probe of the EEPROM size.
|
||||
Options: Probed, 2k, 8k, 16k.
|
||||
(Probing works for 2k and 8k but not that well for 16k)
|
||||
|
||||
config ETRAX_I2C_EEPROM_2KB
|
||||
bool "2kB"
|
||||
help
|
||||
Use a 2kB EEPROM.
|
||||
|
||||
config ETRAX_I2C_EEPROM_8KB
|
||||
bool "8kB"
|
||||
help
|
||||
Use a 8kB EEPROM.
|
||||
|
||||
config ETRAX_I2C_EEPROM_16KB
|
||||
bool "16kB"
|
||||
help
|
||||
Use a 16kB EEPROM.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_GPIO
|
||||
bool "GPIO support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
---help---
|
||||
Enables the ETRAX general port device (major 120, minors 0 and 1).
|
||||
You can use this driver to access the general port bits. It supports
|
||||
these ioctl's:
|
||||
#include <linux/etraxgpio.h>
|
||||
fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
|
||||
val = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS), NULL);
|
||||
Remember that you need to setup the port directions appropriately in
|
||||
the General configuration.
|
||||
|
||||
config ETRAX_PA_CHANGEABLE_DIR
|
||||
hex "PA user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "00"
|
||||
help
|
||||
This is a bitmask with information of what bits in PA that a user
|
||||
can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 00 here.
|
||||
|
||||
config ETRAX_PA_CHANGEABLE_BITS
|
||||
hex "PA user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "FF"
|
||||
help
|
||||
This is a bitmask with information of what bits in PA that a user
|
||||
can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 00 here.
|
||||
|
||||
config ETRAX_PB_CHANGEABLE_DIR
|
||||
hex "PB user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "00"
|
||||
help
|
||||
This is a bitmask with information of what bits in PB that a user
|
||||
can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 00 here.
|
||||
|
||||
config ETRAX_PB_CHANGEABLE_BITS
|
||||
hex "PB user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "FF"
|
||||
help
|
||||
This is a bitmask with information of what bits in PB that a user
|
||||
can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 00 here.
|
||||
|
||||
endif
|
10
arch/cris/arch-v10/drivers/Makefile
Normal file
10
arch/cris/arch-v10/drivers/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
|
||||
obj-$(CONFIG_ETRAX_I2C) += i2c.o
|
||||
obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o
|
||||
obj-$(CONFIG_ETRAX_GPIO) += gpio.o
|
||||
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
|
||||
|
432
arch/cris/arch-v10/drivers/axisflashmap.c
Normal file
432
arch/cris/arch-v10/drivers/axisflashmap.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Physical mapping layer for MTD using the Axis partitiontable format
|
||||
*
|
||||
* Copyright (c) 2001, 2002 Axis Communications AB
|
||||
*
|
||||
* This file is under the GPL.
|
||||
*
|
||||
* First partition is always sector 0 regardless of if we find a partitiontable
|
||||
* or not. In the start of the next sector, there can be a partitiontable that
|
||||
* tells us what other partitions to define. If there isn't, we use a default
|
||||
* partition split defined below.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mtd/concat.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/mtdram.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/axisflashmap.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <arch/sv_addr_ag.h>
|
||||
|
||||
#ifdef CONFIG_CRIS_LOW_MAP
|
||||
#define FLASH_UNCACHED_ADDR KSEG_8
|
||||
#define FLASH_CACHED_ADDR KSEG_5
|
||||
#else
|
||||
#define FLASH_UNCACHED_ADDR KSEG_E
|
||||
#define FLASH_CACHED_ADDR KSEG_F
|
||||
#endif
|
||||
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==1
|
||||
#define flash_data __u8
|
||||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
#define flash_data __u16
|
||||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
|
||||
#define flash_data __u32
|
||||
#endif
|
||||
|
||||
/* From head.S */
|
||||
extern unsigned long romfs_start, romfs_length, romfs_in_flash;
|
||||
|
||||
/* The master mtd for the entire flash. */
|
||||
struct mtd_info* axisflash_mtd = NULL;
|
||||
|
||||
/* Map driver functions. */
|
||||
|
||||
static map_word flash_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word tmp;
|
||||
tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void flash_copy_from(struct map_info *map, void *to,
|
||||
unsigned long from, ssize_t len)
|
||||
{
|
||||
memcpy(to, (void *)(map->map_priv_1 + from), len);
|
||||
}
|
||||
|
||||
static void flash_write(struct map_info *map, map_word d, unsigned long adr)
|
||||
{
|
||||
*(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* The map for chip select e0.
|
||||
*
|
||||
* We run into tricky coherence situations if we mix cached with uncached
|
||||
* accesses to we only use the uncached version here.
|
||||
*
|
||||
* The size field is the total size where the flash chips may be mapped on the
|
||||
* chip select. MTD probes should find all devices there and it does not matter
|
||||
* if there are unmapped gaps or aliases (mirrors of flash devices). The MTD
|
||||
* probes will ignore them.
|
||||
*
|
||||
* The start address in map_priv_1 is in virtual memory so we cannot use
|
||||
* MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start
|
||||
* address of cse0.
|
||||
*/
|
||||
static struct map_info map_cse0 = {
|
||||
.name = "cse0",
|
||||
.size = MEM_CSE0_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR
|
||||
};
|
||||
|
||||
/*
|
||||
* The map for chip select e1.
|
||||
*
|
||||
* If there was a gap between cse0 and cse1, map_priv_1 would get the wrong
|
||||
* address, but there isn't.
|
||||
*/
|
||||
static struct map_info map_cse1 = {
|
||||
.name = "cse1",
|
||||
.size = MEM_CSE1_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
|
||||
};
|
||||
|
||||
/* If no partition-table was found, we use this default-set. */
|
||||
#define MAX_PARTITIONS 7
|
||||
#define NUM_DEFAULT_PARTITIONS 3
|
||||
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
};
|
||||
|
||||
/* Initialize the ones normally used. */
|
||||
static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
|
||||
{
|
||||
.name = "part0",
|
||||
.size = CONFIG_ETRAX_PTABLE_SECTOR,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part1",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part2",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part3",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part4",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part5",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "part6",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
|
||||
/* Main flash device */
|
||||
static struct mtd_partition main_partition = {
|
||||
.name = "main",
|
||||
.size = 0,
|
||||
.offset = 0
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash
|
||||
* chips in that order (because the amd_flash-driver is faster).
|
||||
*/
|
||||
static struct mtd_info *probe_cs(struct map_info *map_cs)
|
||||
{
|
||||
struct mtd_info *mtd_cs = NULL;
|
||||
|
||||
printk(KERN_INFO
|
||||
"%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
|
||||
map_cs->name, map_cs->size, map_cs->map_priv_1);
|
||||
|
||||
#ifdef CONFIG_MTD_CFI
|
||||
mtd_cs = do_map_probe("cfi_probe", map_cs);
|
||||
#endif
|
||||
#ifdef CONFIG_MTD_JEDECPROBE
|
||||
if (!mtd_cs)
|
||||
mtd_cs = do_map_probe("jedec_probe", map_cs);
|
||||
#endif
|
||||
|
||||
return mtd_cs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe each chip select individually for flash chips. If there are chips on
|
||||
* both cse0 and cse1, the mtd_info structs will be concatenated to one struct
|
||||
* so that MTD partitions can cross chip boundries.
|
||||
*
|
||||
* The only known restriction to how you can mount your chips is that each
|
||||
* chip select must hold similar flash chips. But you need external hardware
|
||||
* to do that anyway and you can put totally different chips on cse0 and cse1
|
||||
* so it isn't really much of a restriction.
|
||||
*/
|
||||
static struct mtd_info *flash_probe(void)
|
||||
{
|
||||
struct mtd_info *mtd_cse0;
|
||||
struct mtd_info *mtd_cse1;
|
||||
struct mtd_info *mtd_cse;
|
||||
|
||||
mtd_cse0 = probe_cs(&map_cse0);
|
||||
mtd_cse1 = probe_cs(&map_cse1);
|
||||
|
||||
if (!mtd_cse0 && !mtd_cse1) {
|
||||
/* No chip found. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mtd_cse0 && mtd_cse1) {
|
||||
struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 };
|
||||
|
||||
/* Since the concatenation layer adds a small overhead we
|
||||
* could try to figure out if the chips in cse0 and cse1 are
|
||||
* identical and reprobe the whole cse0+cse1 window. But since
|
||||
* flash chips are slow, the overhead is relatively small.
|
||||
* So we use the MTD concatenation layer instead of further
|
||||
* complicating the probing procedure.
|
||||
*/
|
||||
mtd_cse = mtd_concat_create(mtds, ARRAY_SIZE(mtds),
|
||||
"cse0+cse1");
|
||||
if (!mtd_cse) {
|
||||
printk(KERN_ERR "%s and %s: Concatenation failed!\n",
|
||||
map_cse0.name, map_cse1.name);
|
||||
|
||||
/* The best we can do now is to only use what we found
|
||||
* at cse0.
|
||||
*/
|
||||
mtd_cse = mtd_cse0;
|
||||
map_destroy(mtd_cse1);
|
||||
}
|
||||
} else {
|
||||
mtd_cse = mtd_cse0? mtd_cse0 : mtd_cse1;
|
||||
}
|
||||
|
||||
return mtd_cse;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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[] = " /dev/flash%d at 0x%08x, size 0x%08x\n";
|
||||
|
||||
if (!(mymtd = flash_probe())) {
|
||||
/* There's no reason to use this module if no flash chip can
|
||||
* 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;
|
||||
ptable_head = (struct partitiontable_head *)(FLASH_CACHED_ADDR +
|
||||
CONFIG_ETRAX_PTABLE_SECTOR +
|
||||
PARTITION_TABLE_OFFSET);
|
||||
}
|
||||
pidx++; /* First partition is always set to the default. */
|
||||
|
||||
if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
|
||||
&& (ptable_head->size <
|
||||
(MAX_PARTITIONS * sizeof(struct partitiontable_entry) +
|
||||
PARTITIONTABLE_END_MARKER_SIZE))
|
||||
&& (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) +
|
||||
ptable_head->size -
|
||||
PARTITIONTABLE_END_MARKER_SIZE)
|
||||
== PARTITIONTABLE_END_MARKER)) {
|
||||
/* Looks like a start, sane length and end of a
|
||||
* partition table, lets check csum etc.
|
||||
*/
|
||||
int ptable_ok = 0;
|
||||
struct partitiontable_entry *max_addr =
|
||||
(struct partitiontable_entry *)
|
||||
((unsigned long)ptable_head + sizeof(*ptable_head) +
|
||||
ptable_head->size);
|
||||
unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR;
|
||||
unsigned char *p;
|
||||
unsigned long csum = 0;
|
||||
|
||||
ptable = (struct partitiontable_entry *)
|
||||
((unsigned long)ptable_head + sizeof(*ptable_head));
|
||||
|
||||
/* Lets be PARANOID, and check the checksum. */
|
||||
p = (unsigned char*) ptable;
|
||||
|
||||
while (p <= (unsigned char*)max_addr) {
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
csum += *p++;
|
||||
}
|
||||
ptable_ok = (csum == ptable_head->checksum);
|
||||
|
||||
/* Read the entries and use/show the info. */
|
||||
printk(KERN_INFO " 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.
|
||||
*/
|
||||
while (ptable_ok
|
||||
&& ptable->offset != 0xffffffff
|
||||
&& ptable < max_addr
|
||||
&& pidx < MAX_PARTITIONS) {
|
||||
|
||||
axis_partitions[pidx].offset = offset + ptable->offset;
|
||||
axis_partitions[pidx].size = ptable->size;
|
||||
|
||||
printk(pmsg, pidx, axis_partitions[pidx].offset,
|
||||
axis_partitions[pidx].size);
|
||||
pidx++;
|
||||
ptable++;
|
||||
}
|
||||
use_default_ptable = !ptable_ok;
|
||||
}
|
||||
|
||||
if (romfs_in_flash) {
|
||||
/* Add an overlapping device for the root partition (romfs). */
|
||||
|
||||
axis_partitions[pidx].name = "romfs";
|
||||
axis_partitions[pidx].size = romfs_length;
|
||||
axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
|
||||
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);
|
||||
pidx++;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
|
||||
if (mymtd) {
|
||||
main_partition.size = mymtd->size;
|
||||
err = mtd_device_register(mymtd, &main_partition, 1);
|
||||
if (err)
|
||||
panic("axisflashmap: Could not initialize "
|
||||
"partition for whole main mtd device!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mymtd) {
|
||||
if (use_default_ptable) {
|
||||
printk(KERN_INFO " Using default partition table.\n");
|
||||
err = mtd_device_register(mymtd,
|
||||
axis_default_partitions,
|
||||
NUM_DEFAULT_PARTITIONS);
|
||||
} else {
|
||||
err = mtd_device_register(mymtd, axis_partitions,
|
||||
pidx);
|
||||
}
|
||||
|
||||
if (err)
|
||||
panic("axisflashmap could not add MTD partitions!\n");
|
||||
}
|
||||
|
||||
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 = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
|
||||
if (!mtd_ram)
|
||||
panic("axisflashmap couldn't allocate memory for "
|
||||
"mtd_info!\n");
|
||||
|
||||
printk(KERN_INFO " Adding RAM partition for romfs image:\n");
|
||||
printk(pmsg, pidx, (unsigned)romfs_start,
|
||||
(unsigned)romfs_length);
|
||||
|
||||
err = mtdram_init_device(mtd_ram,
|
||||
(void *)romfs_start,
|
||||
romfs_length,
|
||||
"romfs");
|
||||
if (err)
|
||||
panic("axisflashmap could not initialize MTD RAM "
|
||||
"device!\n");
|
||||
#endif
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This adds the above to the kernels init-call chain. */
|
||||
module_init(init_axis_flash);
|
||||
|
||||
EXPORT_SYMBOL(axisflash_mtd);
|
852
arch/cris/arch-v10/drivers/eeprom.c
Normal file
852
arch/cris/arch-v10/drivers/eeprom.c
Normal file
|
@ -0,0 +1,852 @@
|
|||
/*!*****************************************************************************
|
||||
*!
|
||||
*! Implements an interface for i2c compatible eeproms to run under Linux.
|
||||
*! Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustments by
|
||||
*! Johan.Adolfsson@axis.com
|
||||
*!
|
||||
*! Probing results:
|
||||
*! 8k or not is detected (the assumes 2k or 16k)
|
||||
*! 2k or 16k detected using test reads and writes.
|
||||
*!
|
||||
*!------------------------------------------------------------------------
|
||||
*! HISTORY
|
||||
*!
|
||||
*! DATE NAME CHANGES
|
||||
*! ---- ---- -------
|
||||
*! Aug 28 1999 Edgar Iglesias Initial Version
|
||||
*! Aug 31 1999 Edgar Iglesias Allow simultaneous users.
|
||||
*! Sep 03 1999 Edgar Iglesias Updated probe.
|
||||
*! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted
|
||||
*! in the spin-lock.
|
||||
*!
|
||||
*! (c) 1999 Axis Communications AB, Lund, Sweden
|
||||
*!*****************************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "i2c.h"
|
||||
|
||||
#define D(x)
|
||||
|
||||
/* If we should use adaptive timing or not: */
|
||||
/* #define EEPROM_ADAPTIVE_TIMING */
|
||||
|
||||
#define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */
|
||||
#define EEPROM_MINOR_NR 0
|
||||
|
||||
/* Empirical sane initial value of the delay, the value will be adapted to
|
||||
* what the chip needs when using EEPROM_ADAPTIVE_TIMING.
|
||||
*/
|
||||
#define INITIAL_WRITEDELAY_US 4000
|
||||
#define MAX_WRITEDELAY_US 10000 /* 10 ms according to spec for 2KB EEPROM */
|
||||
|
||||
/* This one defines how many times to try when eeprom fails. */
|
||||
#define EEPROM_RETRIES 10
|
||||
|
||||
#define EEPROM_2KB (2 * 1024)
|
||||
/*#define EEPROM_4KB (4 * 1024)*/ /* Exists but not used in Axis products */
|
||||
#define EEPROM_8KB (8 * 1024 - 1 ) /* Last byte has write protection bit */
|
||||
#define EEPROM_16KB (16 * 1024)
|
||||
|
||||
#define i2c_delay(x) udelay(x)
|
||||
|
||||
/*
|
||||
* This structure describes the attached eeprom chip.
|
||||
* The values are probed for.
|
||||
*/
|
||||
|
||||
struct eeprom_type
|
||||
{
|
||||
unsigned long size;
|
||||
unsigned long sequential_write_pagesize;
|
||||
unsigned char select_cmd;
|
||||
unsigned long usec_delay_writecycles; /* Min time between write cycles
|
||||
(up to 10ms for some models) */
|
||||
unsigned long usec_delay_step; /* For adaptive algorithm */
|
||||
int adapt_state; /* 1 = To high , 0 = Even, -1 = To low */
|
||||
|
||||
/* this one is to keep the read/write operations atomic */
|
||||
struct mutex lock;
|
||||
int retry_cnt_addr; /* Used to keep track of number of retries for
|
||||
adaptive timing adjustments */
|
||||
int retry_cnt_read;
|
||||
};
|
||||
|
||||
static int eeprom_open(struct inode * inode, struct file * file);
|
||||
static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig);
|
||||
static ssize_t eeprom_read(struct file * file, char * buf, size_t count,
|
||||
loff_t *off);
|
||||
static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
|
||||
loff_t *off);
|
||||
static int eeprom_close(struct inode * inode, struct file * file);
|
||||
|
||||
static int eeprom_address(unsigned long addr);
|
||||
static int read_from_eeprom(char * buf, int count);
|
||||
static int eeprom_write_buf(loff_t addr, const char * buf, int count);
|
||||
static int eeprom_read_buf(loff_t addr, char * buf, int count);
|
||||
|
||||
static void eeprom_disable_write_protect(void);
|
||||
|
||||
|
||||
static const char eeprom_name[] = "eeprom";
|
||||
|
||||
/* chip description */
|
||||
static struct eeprom_type eeprom;
|
||||
|
||||
/* This is the exported file-operations structure for this device. */
|
||||
const struct file_operations eeprom_fops =
|
||||
{
|
||||
.llseek = eeprom_lseek,
|
||||
.read = eeprom_read,
|
||||
.write = eeprom_write,
|
||||
.open = eeprom_open,
|
||||
.release = eeprom_close
|
||||
};
|
||||
|
||||
/* eeprom init call. Probes for different eeprom models. */
|
||||
|
||||
int __init eeprom_init(void)
|
||||
{
|
||||
mutex_init(&eeprom.lock);
|
||||
|
||||
#ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE
|
||||
#define EETEXT "Found"
|
||||
#else
|
||||
#define EETEXT "Assuming"
|
||||
#endif
|
||||
if (register_chrdev(EEPROM_MAJOR_NR, eeprom_name, &eeprom_fops))
|
||||
{
|
||||
printk(KERN_INFO "%s: unable to get major %d for eeprom device\n",
|
||||
eeprom_name, EEPROM_MAJOR_NR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk("EEPROM char device v0.3, (c) 2000 Axis Communications AB\n");
|
||||
|
||||
/*
|
||||
* Note: Most of this probing method was taken from the printserver (5470e)
|
||||
* codebase. It did not contain a way of finding the 16kB chips
|
||||
* (M24128 or variants). The method used here might not work
|
||||
* for all models. If you encounter problems the easiest way
|
||||
* is probably to define your model within #ifdef's, and hard-
|
||||
* code it.
|
||||
*/
|
||||
|
||||
eeprom.size = 0;
|
||||
eeprom.usec_delay_writecycles = INITIAL_WRITEDELAY_US;
|
||||
eeprom.usec_delay_step = 128;
|
||||
eeprom.adapt_state = 0;
|
||||
|
||||
#ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE
|
||||
i2c_start();
|
||||
i2c_outbyte(0x80);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
/* It's not 8k.. */
|
||||
int success = 0;
|
||||
unsigned char buf_2k_start[16];
|
||||
|
||||
/* Im not sure this will work... :) */
|
||||
/* assume 2kB, if failure go for 16kB */
|
||||
/* Test with 16kB settings.. */
|
||||
/* If it's a 2kB EEPROM and we address it outside it's range
|
||||
* it will mirror the address space:
|
||||
* 1. We read two locations (that are mirrored),
|
||||
* if the content differs * it's a 16kB EEPROM.
|
||||
* 2. if it doesn't differ - write different value to one of the locations,
|
||||
* check the other - if content still is the same it's a 2k EEPROM,
|
||||
* restore original data.
|
||||
*/
|
||||
#define LOC1 8
|
||||
#define LOC2 (0x1fb) /*1fb, 3ed, 5df, 7d1 */
|
||||
|
||||
/* 2k settings */
|
||||
i2c_stop();
|
||||
eeprom.size = EEPROM_2KB;
|
||||
eeprom.select_cmd = 0xA0;
|
||||
eeprom.sequential_write_pagesize = 16;
|
||||
if( eeprom_read_buf( 0, buf_2k_start, 16 ) == 16 )
|
||||
{
|
||||
D(printk("2k start: '%16.16s'\n", buf_2k_start));
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: Failed to read in 2k mode!\n", eeprom_name);
|
||||
}
|
||||
|
||||
/* 16k settings */
|
||||
eeprom.size = EEPROM_16KB;
|
||||
eeprom.select_cmd = 0xA0;
|
||||
eeprom.sequential_write_pagesize = 64;
|
||||
|
||||
{
|
||||
unsigned char loc1[4], loc2[4], tmp[4];
|
||||
if( eeprom_read_buf(LOC2, loc2, 4) == 4)
|
||||
{
|
||||
if( eeprom_read_buf(LOC1, loc1, 4) == 4)
|
||||
{
|
||||
D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
|
||||
LOC1, loc1, LOC2, loc2));
|
||||
#if 0
|
||||
if (memcmp(loc1, loc2, 4) != 0 )
|
||||
{
|
||||
/* It's 16k */
|
||||
printk(KERN_INFO "%s: 16k detected in step 1\n", eeprom_name);
|
||||
eeprom.size = EEPROM_16KB;
|
||||
success = 1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Do step 2 check */
|
||||
/* Invert value */
|
||||
loc1[0] = ~loc1[0];
|
||||
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
|
||||
{
|
||||
/* If 2k EEPROM this write will actually write 10 bytes
|
||||
* from pos 0
|
||||
*/
|
||||
D(printk("1 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
|
||||
LOC1, loc1, LOC2, loc2));
|
||||
if( eeprom_read_buf(LOC1, tmp, 4) == 4)
|
||||
{
|
||||
D(printk("2 loc1: (%i) '%4.4s' tmp '%4.4s'\n",
|
||||
LOC1, loc1, tmp));
|
||||
if (memcmp(loc1, tmp, 4) != 0 )
|
||||
{
|
||||
printk(KERN_INFO "%s: read and write differs! Not 16kB\n",
|
||||
eeprom_name);
|
||||
loc1[0] = ~loc1[0];
|
||||
|
||||
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
|
||||
{
|
||||
success = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: Restore 2k failed during probe,"
|
||||
" EEPROM might be corrupt!\n", eeprom_name);
|
||||
|
||||
}
|
||||
i2c_stop();
|
||||
/* Go to 2k mode and write original data */
|
||||
eeprom.size = EEPROM_2KB;
|
||||
eeprom.select_cmd = 0xA0;
|
||||
eeprom.sequential_write_pagesize = 16;
|
||||
if( eeprom_write_buf(0, buf_2k_start, 16) == 16)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: Failed to write back 2k start!\n",
|
||||
eeprom_name);
|
||||
}
|
||||
|
||||
eeprom.size = EEPROM_2KB;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
{
|
||||
if( eeprom_read_buf(LOC2, loc2, 1) == 1)
|
||||
{
|
||||
D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
|
||||
LOC1, loc1, LOC2, loc2));
|
||||
if (memcmp(loc1, loc2, 4) == 0 )
|
||||
{
|
||||
/* Data the same, must be mirrored -> 2k */
|
||||
/* Restore data */
|
||||
printk(KERN_INFO "%s: 2k detected in step 2\n", eeprom_name);
|
||||
loc1[0] = ~loc1[0];
|
||||
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
|
||||
{
|
||||
success = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: Restore 2k failed during probe,"
|
||||
" EEPROM might be corrupt!\n", eeprom_name);
|
||||
|
||||
}
|
||||
|
||||
eeprom.size = EEPROM_2KB;
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: 16k detected in step 2\n",
|
||||
eeprom_name);
|
||||
loc1[0] = ~loc1[0];
|
||||
/* Data differs, assume 16k */
|
||||
/* Restore data */
|
||||
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
|
||||
{
|
||||
success = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printk(KERN_INFO "%s: Restore 16k failed during probe,"
|
||||
" EEPROM might be corrupt!\n", eeprom_name);
|
||||
}
|
||||
|
||||
eeprom.size = EEPROM_16KB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* read LOC1 */
|
||||
} /* address LOC1 */
|
||||
if (!success)
|
||||
{
|
||||
printk(KERN_INFO "%s: Probing failed!, using 2KB!\n", eeprom_name);
|
||||
eeprom.size = EEPROM_2KB;
|
||||
}
|
||||
} /* read */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i2c_outbyte(0x00);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
/* No 8k */
|
||||
eeprom.size = EEPROM_2KB;
|
||||
}
|
||||
else
|
||||
{
|
||||
i2c_start();
|
||||
i2c_outbyte(0x81);
|
||||
if (!i2c_getack())
|
||||
{
|
||||
eeprom.size = EEPROM_2KB;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a 8kB */
|
||||
i2c_inbyte();
|
||||
eeprom.size = EEPROM_8KB;
|
||||
}
|
||||
}
|
||||
}
|
||||
i2c_stop();
|
||||
#elif defined(CONFIG_ETRAX_I2C_EEPROM_16KB)
|
||||
eeprom.size = EEPROM_16KB;
|
||||
#elif defined(CONFIG_ETRAX_I2C_EEPROM_8KB)
|
||||
eeprom.size = EEPROM_8KB;
|
||||
#elif defined(CONFIG_ETRAX_I2C_EEPROM_2KB)
|
||||
eeprom.size = EEPROM_2KB;
|
||||
#endif
|
||||
|
||||
switch(eeprom.size)
|
||||
{
|
||||
case (EEPROM_2KB):
|
||||
printk("%s: " EETEXT " i2c compatible 2kB eeprom.\n", eeprom_name);
|
||||
eeprom.sequential_write_pagesize = 16;
|
||||
eeprom.select_cmd = 0xA0;
|
||||
break;
|
||||
case (EEPROM_8KB):
|
||||
printk("%s: " EETEXT " i2c compatible 8kB eeprom.\n", eeprom_name);
|
||||
eeprom.sequential_write_pagesize = 16;
|
||||
eeprom.select_cmd = 0x80;
|
||||
break;
|
||||
case (EEPROM_16KB):
|
||||
printk("%s: " EETEXT " i2c compatible 16kB eeprom.\n", eeprom_name);
|
||||
eeprom.sequential_write_pagesize = 64;
|
||||
eeprom.select_cmd = 0xA0;
|
||||
break;
|
||||
default:
|
||||
eeprom.size = 0;
|
||||
printk("%s: Did not find a supported eeprom\n", eeprom_name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
eeprom_disable_write_protect();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Opens the device. */
|
||||
static int eeprom_open(struct inode * inode, struct file * file)
|
||||
{
|
||||
if(iminor(inode) != EEPROM_MINOR_NR)
|
||||
return -ENXIO;
|
||||
if(imajor(inode) != EEPROM_MAJOR_NR)
|
||||
return -ENXIO;
|
||||
|
||||
if( eeprom.size > 0 )
|
||||
{
|
||||
/* OK */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No EEprom found */
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Changes the current file position. */
|
||||
|
||||
static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig)
|
||||
{
|
||||
/*
|
||||
* orig 0: position from begning of eeprom
|
||||
* orig 1: relative from current position
|
||||
* orig 2: position from last eeprom address
|
||||
*/
|
||||
|
||||
switch (orig)
|
||||
{
|
||||
case 0:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
case 1:
|
||||
file->f_pos += offset;
|
||||
break;
|
||||
case 2:
|
||||
file->f_pos = eeprom.size - offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* truncate position */
|
||||
if (file->f_pos < 0)
|
||||
{
|
||||
file->f_pos = 0;
|
||||
return(-EOVERFLOW);
|
||||
}
|
||||
|
||||
if (file->f_pos >= eeprom.size)
|
||||
{
|
||||
file->f_pos = eeprom.size - 1;
|
||||
return(-EOVERFLOW);
|
||||
}
|
||||
|
||||
return ( file->f_pos );
|
||||
}
|
||||
|
||||
/* Reads data from eeprom. */
|
||||
|
||||
static int eeprom_read_buf(loff_t addr, char * buf, int count)
|
||||
{
|
||||
return eeprom_read(NULL, buf, count, &addr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Reads data from eeprom. */
|
||||
|
||||
static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off)
|
||||
{
|
||||
int read=0;
|
||||
unsigned long p = *off;
|
||||
|
||||
unsigned char page;
|
||||
|
||||
if(p >= eeprom.size) /* Address i 0 - (size-1) */
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&eeprom.lock))
|
||||
return -EINTR;
|
||||
|
||||
page = (unsigned char) (p >> 8);
|
||||
|
||||
if(!eeprom_address(p))
|
||||
{
|
||||
printk(KERN_INFO "%s: Read failed to address the eeprom: "
|
||||
"0x%08X (%i) page: %i\n", eeprom_name, (int)p, (int)p, page);
|
||||
i2c_stop();
|
||||
|
||||
/* don't forget to wake them up */
|
||||
mutex_unlock(&eeprom.lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if( (p + count) > eeprom.size)
|
||||
{
|
||||
/* truncate count */
|
||||
count = eeprom.size - p;
|
||||
}
|
||||
|
||||
/* stop dummy write op and initiate the read op */
|
||||
i2c_start();
|
||||
|
||||
/* special case for small eeproms */
|
||||
if(eeprom.size < EEPROM_16KB)
|
||||
{
|
||||
i2c_outbyte( eeprom.select_cmd | 1 | (page << 1) );
|
||||
}
|
||||
|
||||
/* go on with the actual read */
|
||||
read = read_from_eeprom( buf, count);
|
||||
|
||||
if(read > 0)
|
||||
{
|
||||
*off += read;
|
||||
}
|
||||
|
||||
mutex_unlock(&eeprom.lock);
|
||||
return read;
|
||||
}
|
||||
|
||||
/* Writes data to eeprom. */
|
||||
|
||||
static int eeprom_write_buf(loff_t addr, const char * buf, int count)
|
||||
{
|
||||
return eeprom_write(NULL, buf, count, &addr);
|
||||
}
|
||||
|
||||
|
||||
/* Writes data to eeprom. */
|
||||
|
||||
static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
|
||||
loff_t *off)
|
||||
{
|
||||
int i, written, restart=1;
|
||||
unsigned long p;
|
||||
|
||||
if (!access_ok(VERIFY_READ, buf, count))
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* bail out if we get interrupted */
|
||||
if (mutex_lock_interruptible(&eeprom.lock))
|
||||
return -EINTR;
|
||||
for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++)
|
||||
{
|
||||
restart = 0;
|
||||
written = 0;
|
||||
p = *off;
|
||||
|
||||
|
||||
while( (written < count) && (p < eeprom.size))
|
||||
{
|
||||
/* address the eeprom */
|
||||
if(!eeprom_address(p))
|
||||
{
|
||||
printk(KERN_INFO "%s: Write failed to address the eeprom: "
|
||||
"0x%08X (%i) \n", eeprom_name, (int)p, (int)p);
|
||||
i2c_stop();
|
||||
|
||||
/* don't forget to wake them up */
|
||||
mutex_unlock(&eeprom.lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
#ifdef EEPROM_ADAPTIVE_TIMING
|
||||
/* Adaptive algorithm to adjust timing */
|
||||
if (eeprom.retry_cnt_addr > 0)
|
||||
{
|
||||
/* To Low now */
|
||||
D(printk(">D=%i d=%i\n",
|
||||
eeprom.usec_delay_writecycles, eeprom.usec_delay_step));
|
||||
|
||||
if (eeprom.usec_delay_step < 4)
|
||||
{
|
||||
eeprom.usec_delay_step++;
|
||||
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (eeprom.adapt_state > 0)
|
||||
{
|
||||
/* To Low before */
|
||||
eeprom.usec_delay_step *= 2;
|
||||
if (eeprom.usec_delay_step > 2)
|
||||
{
|
||||
eeprom.usec_delay_step--;
|
||||
}
|
||||
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
|
||||
}
|
||||
else if (eeprom.adapt_state < 0)
|
||||
{
|
||||
/* To High before (toggle dir) */
|
||||
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
|
||||
if (eeprom.usec_delay_step > 1)
|
||||
{
|
||||
eeprom.usec_delay_step /= 2;
|
||||
eeprom.usec_delay_step--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eeprom.adapt_state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* To High (or good) now */
|
||||
D(printk("<D=%i d=%i\n",
|
||||
eeprom.usec_delay_writecycles, eeprom.usec_delay_step));
|
||||
|
||||
if (eeprom.adapt_state < 0)
|
||||
{
|
||||
/* To High before */
|
||||
if (eeprom.usec_delay_step > 1)
|
||||
{
|
||||
eeprom.usec_delay_step *= 2;
|
||||
eeprom.usec_delay_step--;
|
||||
|
||||
if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step)
|
||||
{
|
||||
eeprom.usec_delay_writecycles -= eeprom.usec_delay_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eeprom.adapt_state > 0)
|
||||
{
|
||||
/* To Low before (toggle dir) */
|
||||
if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step)
|
||||
{
|
||||
eeprom.usec_delay_writecycles -= eeprom.usec_delay_step;
|
||||
}
|
||||
if (eeprom.usec_delay_step > 1)
|
||||
{
|
||||
eeprom.usec_delay_step /= 2;
|
||||
eeprom.usec_delay_step--;
|
||||
}
|
||||
|
||||
eeprom.adapt_state = -1;
|
||||
}
|
||||
|
||||
if (eeprom.adapt_state > -100)
|
||||
{
|
||||
eeprom.adapt_state--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Restart adaption */
|
||||
D(printk("#Restart\n"));
|
||||
eeprom.usec_delay_step++;
|
||||
}
|
||||
}
|
||||
#endif /* EEPROM_ADAPTIVE_TIMING */
|
||||
/* write until we hit a page boundary or count */
|
||||
do
|
||||
{
|
||||
i2c_outbyte(buf[written]);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
restart=1;
|
||||
printk(KERN_INFO "%s: write error, retrying. %d\n", eeprom_name, i);
|
||||
i2c_stop();
|
||||
break;
|
||||
}
|
||||
written++;
|
||||
p++;
|
||||
} while( written < count && ( p % eeprom.sequential_write_pagesize ));
|
||||
|
||||
/* end write cycle */
|
||||
i2c_stop();
|
||||
i2c_delay(eeprom.usec_delay_writecycles);
|
||||
} /* while */
|
||||
} /* for */
|
||||
|
||||
mutex_unlock(&eeprom.lock);
|
||||
if (written == 0 && p >= eeprom.size){
|
||||
return -ENOSPC;
|
||||
}
|
||||
*off = p;
|
||||
return written;
|
||||
}
|
||||
|
||||
/* Closes the device. */
|
||||
|
||||
static int eeprom_close(struct inode * inode, struct file * file)
|
||||
{
|
||||
/* do nothing for now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets the current address of the eeprom. */
|
||||
|
||||
static int eeprom_address(unsigned long addr)
|
||||
{
|
||||
int i;
|
||||
unsigned char page, offset;
|
||||
|
||||
page = (unsigned char) (addr >> 8);
|
||||
offset = (unsigned char) addr;
|
||||
|
||||
for(i = 0; i < EEPROM_RETRIES; i++)
|
||||
{
|
||||
/* start a dummy write for addressing */
|
||||
i2c_start();
|
||||
|
||||
if(eeprom.size == EEPROM_16KB)
|
||||
{
|
||||
i2c_outbyte( eeprom.select_cmd );
|
||||
i2c_getack();
|
||||
i2c_outbyte(page);
|
||||
}
|
||||
else
|
||||
{
|
||||
i2c_outbyte( eeprom.select_cmd | (page << 1) );
|
||||
}
|
||||
if(!i2c_getack())
|
||||
{
|
||||
/* retry */
|
||||
i2c_stop();
|
||||
/* Must have a delay here.. 500 works, >50, 100->works 5th time*/
|
||||
i2c_delay(MAX_WRITEDELAY_US / EEPROM_RETRIES * i);
|
||||
/* The chip needs up to 10 ms from write stop to next start */
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
i2c_outbyte(offset);
|
||||
|
||||
if(!i2c_getack())
|
||||
{
|
||||
/* retry */
|
||||
i2c_stop();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
eeprom.retry_cnt_addr = i;
|
||||
D(printk("%i\n", eeprom.retry_cnt_addr));
|
||||
if(eeprom.retry_cnt_addr == EEPROM_RETRIES)
|
||||
{
|
||||
/* failed */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Reads from current address. */
|
||||
|
||||
static int read_from_eeprom(char * buf, int count)
|
||||
{
|
||||
int i, read=0;
|
||||
|
||||
for(i = 0; i < EEPROM_RETRIES; i++)
|
||||
{
|
||||
if(eeprom.size == EEPROM_16KB)
|
||||
{
|
||||
i2c_outbyte( eeprom.select_cmd | 1 );
|
||||
}
|
||||
|
||||
if(i2c_getack())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == EEPROM_RETRIES)
|
||||
{
|
||||
printk(KERN_INFO "%s: failed to read from eeprom\n", eeprom_name);
|
||||
i2c_stop();
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
while( (read < count))
|
||||
{
|
||||
if (put_user(i2c_inbyte(), &buf[read++]))
|
||||
{
|
||||
i2c_stop();
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure we don't ack last byte or you will get very strange
|
||||
* results!
|
||||
*/
|
||||
if(read < count)
|
||||
{
|
||||
i2c_sendack();
|
||||
}
|
||||
}
|
||||
|
||||
/* stop the operation */
|
||||
i2c_stop();
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
/* Disables write protection if applicable. */
|
||||
|
||||
#define DBP_SAVE(x)
|
||||
#define ax_printf printk
|
||||
static void eeprom_disable_write_protect(void)
|
||||
{
|
||||
/* Disable write protect */
|
||||
if (eeprom.size == EEPROM_8KB)
|
||||
{
|
||||
/* Step 1 Set WEL = 1 (write 00000010 to address 1FFFh */
|
||||
i2c_start();
|
||||
i2c_outbyte(0xbe);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false\n"));
|
||||
}
|
||||
i2c_outbyte(0xFF);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 2\n"));
|
||||
}
|
||||
i2c_outbyte(0x02);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 3\n"));
|
||||
}
|
||||
i2c_stop();
|
||||
|
||||
i2c_delay(1000);
|
||||
|
||||
/* Step 2 Set RWEL = 1 (write 00000110 to address 1FFFh */
|
||||
i2c_start();
|
||||
i2c_outbyte(0xbe);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 55\n"));
|
||||
}
|
||||
i2c_outbyte(0xFF);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 52\n"));
|
||||
}
|
||||
i2c_outbyte(0x06);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 53\n"));
|
||||
}
|
||||
i2c_stop();
|
||||
|
||||
/* Step 3 Set BP1, BP0, and/or WPEN bits (write 00000110 to address 1FFFh */
|
||||
i2c_start();
|
||||
i2c_outbyte(0xbe);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 56\n"));
|
||||
}
|
||||
i2c_outbyte(0xFF);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 57\n"));
|
||||
}
|
||||
i2c_outbyte(0x06);
|
||||
if(!i2c_getack())
|
||||
{
|
||||
DBP_SAVE(ax_printf("Get ack returns false 58\n"));
|
||||
}
|
||||
i2c_stop();
|
||||
|
||||
/* Write protect disabled */
|
||||
}
|
||||
}
|
||||
|
||||
module_init(eeprom_init);
|
856
arch/cris/arch-v10/drivers/gpio.c
Normal file
856
arch/cris/arch-v10/drivers/gpio.c
Normal file
|
@ -0,0 +1,856 @@
|
|||
/*
|
||||
* Etrax general port I/O device
|
||||
*
|
||||
* Copyright (c) 1999-2007 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (initial version)
|
||||
* Ola Knutsson (LED handling)
|
||||
* Johan Adolfsson (read/set directions, write, port G)
|
||||
*/
|
||||
|
||||
|
||||
#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 <asm/etraxgpio.h>
|
||||
#include <arch/svinto.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <arch/io_interface_mux.h>
|
||||
|
||||
#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 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;
|
||||
/* These fields are for PA and PB only */
|
||||
volatile unsigned char *port, *shadow;
|
||||
volatile unsigned char *dir, *dir_shadow;
|
||||
unsigned char changeable_dir;
|
||||
unsigned char changeable_bits;
|
||||
unsigned char clk_mask;
|
||||
unsigned char data_mask;
|
||||
unsigned char write_msb;
|
||||
unsigned char pad1, pad2, pad3;
|
||||
/* 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_irq_enabled_mask;
|
||||
|
||||
static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
|
||||
|
||||
/* Port A and B use 8 bit access, but Port G is 32 bit */
|
||||
#define NUM_PORTS (GPIO_MINOR_B+1)
|
||||
|
||||
static volatile unsigned char *ports[NUM_PORTS] = {
|
||||
R_PORT_PA_DATA,
|
||||
R_PORT_PB_DATA,
|
||||
};
|
||||
static volatile unsigned char *shads[NUM_PORTS] = {
|
||||
&port_pa_data_shadow,
|
||||
&port_pb_data_shadow
|
||||
};
|
||||
|
||||
/* What direction bits that are user changeable 1=changeable*/
|
||||
#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
|
||||
#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
|
||||
#endif
|
||||
#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
|
||||
#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
|
||||
#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
|
||||
#endif
|
||||
#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
|
||||
#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
|
||||
#endif
|
||||
|
||||
|
||||
static unsigned char changeable_dir[NUM_PORTS] = {
|
||||
CONFIG_ETRAX_PA_CHANGEABLE_DIR,
|
||||
CONFIG_ETRAX_PB_CHANGEABLE_DIR
|
||||
};
|
||||
static unsigned char changeable_bits[NUM_PORTS] = {
|
||||
CONFIG_ETRAX_PA_CHANGEABLE_BITS,
|
||||
CONFIG_ETRAX_PB_CHANGEABLE_BITS
|
||||
};
|
||||
|
||||
static volatile unsigned char *dir[NUM_PORTS] = {
|
||||
R_PORT_PA_DIR,
|
||||
R_PORT_PB_DIR
|
||||
};
|
||||
|
||||
static volatile unsigned char *dir_shadow[NUM_PORTS] = {
|
||||
&port_pa_dir_shadow,
|
||||
&port_pb_dir_shadow
|
||||
};
|
||||
|
||||
/* All bits in port g that can change dir. */
|
||||
static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
|
||||
|
||||
/* Port G is 32 bit, handle it special, some bits are both inputs
|
||||
and outputs at the same time, only some of the bits can change direction
|
||||
and some of them in groups of 8 bit. */
|
||||
static unsigned long changeable_dir_g;
|
||||
static unsigned long dir_g_in_bits;
|
||||
static unsigned long dir_g_out_bits;
|
||||
static unsigned long dir_g_shadow; /* 1=output */
|
||||
|
||||
#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
|
||||
|
||||
|
||||
static unsigned int gpio_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
struct gpio_private *priv = file->private_data;
|
||||
unsigned long data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
poll_wait(file, &priv->alarm_wq, wait);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
unsigned long tmp;
|
||||
data = *R_PORT_PA_DATA;
|
||||
/* PA has support for high level interrupt -
|
||||
* lets activate for those low and with highalarm set
|
||||
*/
|
||||
tmp = ~data & priv->highalarm & 0xFF;
|
||||
tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
|
||||
|
||||
gpio_pa_irq_enabled_mask |= tmp;
|
||||
*R_IRQ_MASK1_SET = tmp;
|
||||
} else if (priv->minor == GPIO_MINOR_B)
|
||||
data = *R_PORT_PB_DATA;
|
||||
else if (priv->minor == GPIO_MINOR_G)
|
||||
data = *R_PORT_G_DATA;
|
||||
else {
|
||||
mask = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((data & priv->highalarm) ||
|
||||
(~data & priv->lowalarm)) {
|
||||
mask = POLLIN|POLLRDNORM;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
DP(printk("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;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
priv = alarmlist;
|
||||
while (priv) {
|
||||
if (USE_PORTS(priv))
|
||||
data = *priv->port;
|
||||
else if (priv->minor == GPIO_MINOR_G)
|
||||
data = *R_PORT_G_DATA;
|
||||
|
||||
if ((data & priv->highalarm) ||
|
||||
(~data & priv->lowalarm)) {
|
||||
DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
|
||||
wake_up_interruptible(&priv->alarm_wq);
|
||||
ret = 1;
|
||||
}
|
||||
priv = priv->next;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
gpio_poll_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
if (gpio_some_alarms) {
|
||||
etrax_gpio_wake_up_check();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
gpio_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
/* Find what PA interrupts are active */
|
||||
tmp = (*R_IRQ_READ1);
|
||||
|
||||
/* Find those that we have enabled */
|
||||
tmp &= gpio_pa_irq_enabled_mask;
|
||||
|
||||
/* Clear them.. */
|
||||
*R_IRQ_MASK1_CLR = tmp;
|
||||
gpio_pa_irq_enabled_mask &= ~tmp;
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
if (gpio_some_alarms)
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static void gpio_write_bit(struct gpio_private *priv,
|
||||
unsigned char data, int bit)
|
||||
{
|
||||
*priv->port = *priv->shadow &= ~(priv->clk_mask);
|
||||
if (data & 1 << bit)
|
||||
*priv->port = *priv->shadow |= priv->data_mask;
|
||||
else
|
||||
*priv->port = *priv->shadow &= ~(priv->data_mask);
|
||||
|
||||
/* For FPGA: min 5.0ns (DCC) before CCLK high */
|
||||
*priv->port = *priv->shadow |= priv->clk_mask;
|
||||
}
|
||||
|
||||
static void gpio_write_byte(struct gpio_private *priv, unsigned char data)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (priv->write_msb)
|
||||
for (i = 7; i >= 0; i--)
|
||||
gpio_write_bit(priv, data, i);
|
||||
else
|
||||
for (i = 0; i <= 7; i++)
|
||||
gpio_write_bit(priv, data, i);
|
||||
}
|
||||
|
||||
static ssize_t gpio_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct gpio_private *priv = file->private_data;
|
||||
unsigned long flags;
|
||||
ssize_t retval = count;
|
||||
|
||||
if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B)
|
||||
return -EFAULT;
|
||||
|
||||
if (!access_ok(VERIFY_READ, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
/* It must have been configured using the IO_CFG_WRITE_MODE */
|
||||
/* Perhaps a better error code? */
|
||||
if (priv->clk_mask == 0 || priv->data_mask == 0) {
|
||||
retval = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X "
|
||||
"clk 0x%02X msb: %i\n",
|
||||
count, priv->data_mask, priv->clk_mask, priv->write_msb));
|
||||
|
||||
while (count--)
|
||||
gpio_write_byte(priv, *buf++);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
gpio_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct gpio_private *priv;
|
||||
int p = iminor(inode);
|
||||
unsigned long flags;
|
||||
|
||||
if (p > GPIO_MINOR_LAST)
|
||||
return -EINVAL;
|
||||
|
||||
priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL);
|
||||
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->minor = p;
|
||||
|
||||
/* initialize the io/alarm struct */
|
||||
|
||||
if (USE_PORTS(priv)) { /* A and B */
|
||||
priv->port = ports[p];
|
||||
priv->shadow = shads[p];
|
||||
priv->dir = dir[p];
|
||||
priv->dir_shadow = dir_shadow[p];
|
||||
priv->changeable_dir = changeable_dir[p];
|
||||
priv->changeable_bits = changeable_bits[p];
|
||||
} else {
|
||||
priv->port = NULL;
|
||||
priv->shadow = NULL;
|
||||
priv->dir = NULL;
|
||||
priv->dir_shadow = NULL;
|
||||
priv->changeable_dir = 0;
|
||||
priv->changeable_bits = 0;
|
||||
}
|
||||
|
||||
priv->highalarm = 0;
|
||||
priv->lowalarm = 0;
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
init_waitqueue_head(&priv->alarm_wq);
|
||||
|
||||
filp->private_data = priv;
|
||||
|
||||
/* link it into our alarmlist */
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
priv->next = alarmlist;
|
||||
alarmlist = priv;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct gpio_private *p;
|
||||
struct gpio_private *todel;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
p = alarmlist;
|
||||
todel = filp->private_data;
|
||||
|
||||
/* 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;
|
||||
while (p) {
|
||||
if (p->highalarm | p->lowalarm) {
|
||||
gpio_some_alarms = 1;
|
||||
goto out;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
gpio_some_alarms = 0;
|
||||
out:
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
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 */
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->dir = *priv->dir_shadow &=
|
||||
~((unsigned char)arg & priv->changeable_dir);
|
||||
return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
|
||||
}
|
||||
|
||||
if (priv->minor != GPIO_MINOR_G)
|
||||
return 0;
|
||||
|
||||
/* We must fiddle with R_GEN_CONFIG to change dir */
|
||||
if (((arg & dir_g_in_bits) != arg) &&
|
||||
(arg & changeable_dir_g)) {
|
||||
arg &= changeable_dir_g;
|
||||
/* Clear bits in genconfig to set to input */
|
||||
if (arg & (1<<0)) {
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
|
||||
dir_g_in_bits |= (1<<0);
|
||||
dir_g_out_bits &= ~(1<<0);
|
||||
}
|
||||
if ((arg & 0x0000FF00) == 0x0000FF00) {
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
|
||||
dir_g_in_bits |= 0x0000FF00;
|
||||
dir_g_out_bits &= ~0x0000FF00;
|
||||
}
|
||||
if ((arg & 0x00FF0000) == 0x00FF0000) {
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
|
||||
dir_g_in_bits |= 0x00FF0000;
|
||||
dir_g_out_bits &= ~0x00FF0000;
|
||||
}
|
||||
if (arg & (1<<24)) {
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
|
||||
dir_g_in_bits |= (1<<24);
|
||||
dir_g_out_bits &= ~(1<<24);
|
||||
}
|
||||
D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
|
||||
"genconfig to 0x%08lX "
|
||||
"in_bits: 0x%08lX "
|
||||
"out_bits: 0x%08lX\n",
|
||||
(unsigned long)genconfig_shadow,
|
||||
dir_g_in_bits, dir_g_out_bits));
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Must be a >120 ns delay before writing this again */
|
||||
|
||||
}
|
||||
return dir_g_in_bits;
|
||||
} /* setget_input */
|
||||
|
||||
unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
|
||||
{
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->dir = *priv->dir_shadow |=
|
||||
((unsigned char)arg & priv->changeable_dir);
|
||||
return *priv->dir_shadow;
|
||||
}
|
||||
if (priv->minor != GPIO_MINOR_G)
|
||||
return 0;
|
||||
|
||||
/* We must fiddle with R_GEN_CONFIG to change dir */
|
||||
if (((arg & dir_g_out_bits) != arg) &&
|
||||
(arg & changeable_dir_g)) {
|
||||
/* Set bits in genconfig to set to output */
|
||||
if (arg & (1<<0)) {
|
||||
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
|
||||
dir_g_out_bits |= (1<<0);
|
||||
dir_g_in_bits &= ~(1<<0);
|
||||
}
|
||||
if ((arg & 0x0000FF00) == 0x0000FF00) {
|
||||
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
|
||||
dir_g_out_bits |= 0x0000FF00;
|
||||
dir_g_in_bits &= ~0x0000FF00;
|
||||
}
|
||||
if ((arg & 0x00FF0000) == 0x00FF0000) {
|
||||
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
|
||||
dir_g_out_bits |= 0x00FF0000;
|
||||
dir_g_in_bits &= ~0x00FF0000;
|
||||
}
|
||||
if (arg & (1<<24)) {
|
||||
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
|
||||
dir_g_out_bits |= (1<<24);
|
||||
dir_g_in_bits &= ~(1<<24);
|
||||
}
|
||||
D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
|
||||
"genconfig to 0x%08lX "
|
||||
"in_bits: 0x%08lX "
|
||||
"out_bits: 0x%08lX\n",
|
||||
(unsigned long)genconfig_shadow,
|
||||
dir_g_in_bits, dir_g_out_bits));
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Must be a >120 ns delay before writing this again */
|
||||
}
|
||||
return dir_g_out_bits & 0x7FFFFFFF;
|
||||
} /* setget_output */
|
||||
|
||||
static int
|
||||
gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
|
||||
|
||||
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long val;
|
||||
int ret = 0;
|
||||
|
||||
struct gpio_private *priv = 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
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (USE_PORTS(priv)) {
|
||||
ret = *priv->port;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
break;
|
||||
case IO_SETBITS:
|
||||
// set changeable bits with a 1 in arg
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->port = *priv->shadow |=
|
||||
((unsigned char)arg & priv->changeable_bits);
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
*R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
break;
|
||||
case IO_CLRBITS:
|
||||
// clear changeable bits with a 1 in arg
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->port = *priv->shadow &=
|
||||
~((unsigned char)arg & priv->changeable_bits);
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
*R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_HIGHALARM:
|
||||
// set alarm when bits with 1 in arg go high
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
priv->highalarm |= arg;
|
||||
gpio_some_alarms = 1;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_LOWALARM:
|
||||
// set alarm when bits with 1 in arg go low
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
priv->lowalarm |= arg;
|
||||
gpio_some_alarms = 1;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_CLRALARM:
|
||||
/* clear alarm for bits with 1 in arg */
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
priv->highalarm &= ~arg;
|
||||
priv->lowalarm &= ~arg;
|
||||
{
|
||||
/* Must update gpio_some_alarms */
|
||||
struct gpio_private *p = alarmlist;
|
||||
int some_alarms;
|
||||
p = alarmlist;
|
||||
some_alarms = 0;
|
||||
while (p) {
|
||||
if (p->highalarm | p->lowalarm) {
|
||||
some_alarms = 1;
|
||||
break;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
gpio_some_alarms = some_alarms;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
|
||||
/* Read direction 0=input 1=output */
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (USE_PORTS(priv)) {
|
||||
ret = *priv->dir_shadow;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
/* Note: Some bits are both in and out,
|
||||
* Those that are dual is set here as well.
|
||||
*/
|
||||
ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
|
||||
/* Set direction 0=unchanged 1=input,
|
||||
* return mask with 1=input
|
||||
*/
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
ret = setget_input(priv, arg) & 0x7FFFFFFF;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
|
||||
/* Set direction 0=unchanged 1=output,
|
||||
* return mask with 1=output
|
||||
*/
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
ret = setget_output(priv, arg) & 0x7FFFFFFF;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_SHUTDOWN:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
SOFT_SHUTDOWN();
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_GET_PWR_BT:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
|
||||
ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
|
||||
#else
|
||||
ret = 0;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_CFG_WRITE_MODE:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
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 & priv->changeable_bits) &&
|
||||
(priv->data_mask & priv->changeable_bits) &&
|
||||
(priv->clk_mask & *priv->dir_shadow) &&
|
||||
(priv->data_mask & *priv->dir_shadow)))
|
||||
{
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
ret = -EPERM;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
break;
|
||||
case IO_READ_INBITS:
|
||||
/* *arg is result of reading the input pins */
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (USE_PORTS(priv)) {
|
||||
val = *priv->port;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
val = *R_PORT_G_DATA;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_READ_OUTBITS:
|
||||
/* *arg is result of reading the output shadow */
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (USE_PORTS(priv)) {
|
||||
val = *priv->shadow;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
val = port_g_data_shadow;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_INPUT:
|
||||
/* bits set in *arg is set to input,
|
||||
* *arg updated with current input pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
|
||||
{
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
val = setget_input(priv, val);
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_OUTPUT:
|
||||
/* bits set in *arg is set to output,
|
||||
* *arg updated with current output pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
val = setget_output(priv, val);
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
default:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (priv->minor == GPIO_MINOR_LEDS)
|
||||
ret = gpio_leds_ioctl(cmd, arg);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
} /* switch */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case IO_LED_SETBIT:
|
||||
CRIS_LED_BIT_SET(arg);
|
||||
break;
|
||||
|
||||
case IO_LED_CLRBIT:
|
||||
CRIS_LED_BIT_CLR(arg);
|
||||
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,
|
||||
};
|
||||
|
||||
static void ioif_watcher(const unsigned int gpio_in_available,
|
||||
const unsigned int gpio_out_available,
|
||||
const unsigned char pa_available,
|
||||
const unsigned char pb_available)
|
||||
{
|
||||
unsigned long int flags;
|
||||
|
||||
D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
|
||||
D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x "
|
||||
"PA: 0x%02x PB: 0x%02x\n",
|
||||
gpio_in_available, gpio_out_available,
|
||||
pa_available, pb_available));
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
dir_g_in_bits = gpio_in_available;
|
||||
dir_g_out_bits = gpio_out_available;
|
||||
|
||||
/* Initialise the dir_g_shadow etc. depending on genconfig */
|
||||
/* 0=input 1=output */
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
|
||||
dir_g_shadow |= (1 << 0);
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
|
||||
dir_g_shadow |= 0x0000FF00;
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
|
||||
dir_g_shadow |= 0x00FF0000;
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
|
||||
dir_g_shadow |= (1 << 24);
|
||||
|
||||
changeable_dir_g = changeable_dir_g_mask;
|
||||
changeable_dir_g &= dir_g_out_bits;
|
||||
changeable_dir_g &= dir_g_in_bits;
|
||||
|
||||
/* Correct the bits that can change direction */
|
||||
dir_g_out_bits &= ~changeable_dir_g;
|
||||
dir_g_out_bits |= dir_g_shadow;
|
||||
dir_g_in_bits &= ~changeable_dir_g;
|
||||
dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX "
|
||||
"val: %08lX\n",
|
||||
dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
|
||||
printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
|
||||
dir_g_shadow, changeable_dir_g);
|
||||
}
|
||||
|
||||
/* main driver initialization routine, called from mem.c */
|
||||
|
||||
static int __init gpio_init(void)
|
||||
{
|
||||
int res;
|
||||
#if defined (CONFIG_ETRAX_CSP0_LEDS)
|
||||
int i;
|
||||
#endif
|
||||
|
||||
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 */
|
||||
#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
|
||||
CRIS_LED_NETWORK_SET(0);
|
||||
CRIS_LED_ACTIVE_SET(0);
|
||||
CRIS_LED_DISK_READ(0);
|
||||
CRIS_LED_DISK_WRITE(0);
|
||||
|
||||
#if defined (CONFIG_ETRAX_CSP0_LEDS)
|
||||
for (i = 0; i < 32; i++)
|
||||
CRIS_LED_BIT_SET(i);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/* The I/O interface allocation watcher will be called when
|
||||
* registering it. */
|
||||
if (cris_io_interface_register_watcher(ioif_watcher)){
|
||||
printk(KERN_WARNING "gpio_init: Failed to install IO "
|
||||
"if allocator watcher\n");
|
||||
}
|
||||
|
||||
printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 "
|
||||
"Axis Communications AB\n");
|
||||
/* We call etrax_gpio_wake_up_check() from timer interrupt and
|
||||
* from default_idle() in kernel/process.c
|
||||
* The check in default_idle() reduces latency from ~15 ms to ~6 ms
|
||||
* in some tests.
|
||||
*/
|
||||
res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
|
||||
IRQF_SHARED, "gpio poll", gpio_name);
|
||||
if (res) {
|
||||
printk(KERN_CRIT "err: timer0 irq for gpio\n");
|
||||
return res;
|
||||
}
|
||||
res = request_irq(PA_IRQ_NBR, gpio_interrupt,
|
||||
IRQF_SHARED, "gpio PA", gpio_name);
|
||||
if (res)
|
||||
printk(KERN_CRIT "err: PA irq for gpio\n");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* this makes sure that gpio_init is called during kernel boot */
|
||||
module_init(gpio_init);
|
||||
|
698
arch/cris/arch-v10/drivers/i2c.c
Normal file
698
arch/cris/arch-v10/drivers/i2c.c
Normal file
|
@ -0,0 +1,698 @@
|
|||
/*!***************************************************************************
|
||||
*!
|
||||
*! FILE NAME : i2c.c
|
||||
*!
|
||||
*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
|
||||
*! kernel modules (i2c_writereg/readreg) and from userspace using
|
||||
*! ioctl()'s
|
||||
*!
|
||||
*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
*!***************************************************************************/
|
||||
|
||||
/****************** INCLUDE FILES SECTION ***********************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/etraxi2c.h>
|
||||
|
||||
#include <arch/svinto.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <arch/io_interface_mux.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
/****************** I2C DEFINITION SECTION *************************/
|
||||
|
||||
#define D(x)
|
||||
|
||||
#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
|
||||
static const char i2c_name[] = "i2c";
|
||||
|
||||
#define CLOCK_LOW_TIME 8
|
||||
#define CLOCK_HIGH_TIME 8
|
||||
#define START_CONDITION_HOLD_TIME 8
|
||||
#define STOP_CONDITION_HOLD_TIME 8
|
||||
#define ENABLE_OUTPUT 0x01
|
||||
#define ENABLE_INPUT 0x00
|
||||
#define I2C_CLOCK_HIGH 1
|
||||
#define I2C_CLOCK_LOW 0
|
||||
#define I2C_DATA_HIGH 1
|
||||
#define I2C_DATA_LOW 0
|
||||
|
||||
#ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
/* Use PB and not PB_I2C */
|
||||
#ifndef CONFIG_ETRAX_I2C_DATA_PORT
|
||||
#define CONFIG_ETRAX_I2C_DATA_PORT 0
|
||||
#endif
|
||||
#ifndef CONFIG_ETRAX_I2C_CLK_PORT
|
||||
#define CONFIG_ETRAX_I2C_CLK_PORT 1
|
||||
#endif
|
||||
|
||||
#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT
|
||||
#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT
|
||||
#define i2c_enable()
|
||||
#define i2c_disable()
|
||||
|
||||
/* enable or disable output-enable, to select output or input on the i2c bus */
|
||||
|
||||
#define i2c_dir_out() \
|
||||
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 1)
|
||||
#define i2c_dir_in() \
|
||||
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0)
|
||||
|
||||
/* control the i2c clock and data signals */
|
||||
|
||||
#define i2c_clk(x) \
|
||||
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x)
|
||||
#define i2c_data(x) \
|
||||
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SDABIT, x)
|
||||
|
||||
/* read a bit from the i2c interface */
|
||||
|
||||
#define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
|
||||
|
||||
#else
|
||||
/* enable or disable the i2c interface */
|
||||
|
||||
#define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))
|
||||
#define i2c_disable() *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_en))
|
||||
|
||||
/* enable or disable output-enable, to select output or input on the i2c bus */
|
||||
|
||||
#define i2c_dir_out() \
|
||||
*R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
|
||||
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1);
|
||||
#define i2c_dir_in() \
|
||||
*R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
|
||||
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0);
|
||||
|
||||
/* control the i2c clock and data signals */
|
||||
|
||||
#define i2c_clk(x) \
|
||||
*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
|
||||
~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))); \
|
||||
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 1, x);
|
||||
|
||||
#define i2c_data(x) \
|
||||
*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
|
||||
~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))); \
|
||||
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 0, x);
|
||||
|
||||
/* read a bit from the i2c interface */
|
||||
|
||||
#define i2c_getbit() (*R_PORT_PB_READ & 0x1)
|
||||
#endif
|
||||
|
||||
/* use the kernels delay routine */
|
||||
|
||||
#define i2c_delay(usecs) udelay(usecs)
|
||||
|
||||
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
|
||||
|
||||
/****************** FUNCTION DEFINITION SECTION *************************/
|
||||
|
||||
|
||||
/* generate i2c start condition */
|
||||
|
||||
void
|
||||
i2c_start(void)
|
||||
{
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(START_CONDITION_HOLD_TIME);
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
}
|
||||
|
||||
/* generate i2c stop condition */
|
||||
|
||||
void
|
||||
i2c_stop(void)
|
||||
{
|
||||
i2c_dir_out();
|
||||
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(STOP_CONDITION_HOLD_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* write a byte to the i2c interface */
|
||||
|
||||
void
|
||||
i2c_outbyte(unsigned char x)
|
||||
{
|
||||
int i;
|
||||
|
||||
i2c_dir_out();
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (x & 0x80) {
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
} else {
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
}
|
||||
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
x <<= 1;
|
||||
}
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* read a byte from the i2c interface */
|
||||
|
||||
unsigned char
|
||||
i2c_inbyte(void)
|
||||
{
|
||||
unsigned char aBitByte = 0;
|
||||
int i;
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
for (i = 1; i < 8; i++) {
|
||||
aBitByte <<= 1;
|
||||
/* Clock pulse */
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
}
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
|
||||
/*
|
||||
* we leave the clock low, getbyte is usually followed
|
||||
* by sendack/nack, they assume the clock to be low
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
return aBitByte;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_getack
|
||||
*#
|
||||
*# DESCRIPTION : checks if ack was received from ic2
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
i2c_getack(void)
|
||||
{
|
||||
int ack = 1;
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* Release data bus by setting
|
||||
* data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* generate ACK clock pulse
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
/*
|
||||
* Use PORT PB instead of I2C
|
||||
* for input. (I2C not working)
|
||||
*/
|
||||
i2c_clk(1);
|
||||
i2c_data(1);
|
||||
/*
|
||||
* switch off I2C
|
||||
*/
|
||||
i2c_data(1);
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
/*
|
||||
* now wait for ack
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
/*
|
||||
* check for ack
|
||||
*/
|
||||
if(i2c_getbit())
|
||||
ack = 0;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
if(!ack){
|
||||
if(!i2c_getbit()) /* receiver pulld SDA low */
|
||||
ack = 1;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
}
|
||||
|
||||
/*
|
||||
* our clock is high now, make sure data is low
|
||||
* before we enable our output. If we keep data high
|
||||
* and enable output, we would generate a stop condition.
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
|
||||
/*
|
||||
* end clock pulse
|
||||
*/
|
||||
i2c_enable();
|
||||
i2c_dir_out();
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* remove ACK clock pulse
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
return ack;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: I2C::sendAck
|
||||
*#
|
||||
*# DESCRIPTION : Send ACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set ack pulse high
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/6);
|
||||
/*
|
||||
* reset data out
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_sendnack
|
||||
*#
|
||||
*# DESCRIPTION : Sends NACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendnack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_writereg
|
||||
*#
|
||||
*# DESCRIPTION : Writes a value to an I2C device
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
int
|
||||
i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
||||
unsigned char theValue)
|
||||
{
|
||||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&i2c_lock);
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
i2c_start();
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte((theSlave & 0xfe));
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 2;
|
||||
/*
|
||||
* send register register data
|
||||
*/
|
||||
i2c_outbyte(theValue);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 4;
|
||||
/*
|
||||
* end byte stream
|
||||
*/
|
||||
i2c_stop();
|
||||
/*
|
||||
* enable interrupt again
|
||||
*/
|
||||
local_irq_restore(flags);
|
||||
|
||||
} while(error && cntr--);
|
||||
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
spin_unlock(&i2c_lock);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_readreg
|
||||
*#
|
||||
*# DESCRIPTION : Reads a value from the decoder registers.
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
unsigned char
|
||||
i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
||||
{
|
||||
unsigned char b = 0;
|
||||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&i2c_lock);
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
/*
|
||||
* generate start condition
|
||||
*/
|
||||
i2c_start();
|
||||
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte((theSlave & 0xfe));
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* repeat start condition
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_start();
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte(theSlave | 0x01);
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* fetch register
|
||||
*/
|
||||
b = i2c_inbyte();
|
||||
/*
|
||||
* last received byte needs to be nacked
|
||||
* instead of acked
|
||||
*/
|
||||
i2c_sendnack();
|
||||
/*
|
||||
* end sequence
|
||||
*/
|
||||
i2c_stop();
|
||||
/*
|
||||
* enable interrupt again
|
||||
*/
|
||||
local_irq_restore(flags);
|
||||
|
||||
} while(error && cntr--);
|
||||
|
||||
spin_unlock(&i2c_lock);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main device API. ioctl's to write or read to/from i2c registers.
|
||||
*/
|
||||
|
||||
static long i2c_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case I2C_WRITEREG:
|
||||
/* write to an i2c slave */
|
||||
D(printk(KERN_DEBUG "i2cw %d %d %d\n",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg)));
|
||||
|
||||
return i2c_writereg(I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg));
|
||||
case I2C_READREG:
|
||||
{
|
||||
unsigned char val;
|
||||
/* read from an i2c slave */
|
||||
D(printk(KERN_DEBUG "i2cr %d %d ",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg)));
|
||||
val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
|
||||
D(printk(KERN_DEBUG "= %d\n", val));
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations i2c_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = i2c_ioctl,
|
||||
.open = i2c_open,
|
||||
.release = i2c_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
int __init
|
||||
i2c_init(void)
|
||||
{
|
||||
static int res = 0;
|
||||
static int first = 1;
|
||||
|
||||
if (!first) {
|
||||
return res;
|
||||
}
|
||||
first = 0;
|
||||
|
||||
/* Setup and enable the Port B I2C interface */
|
||||
|
||||
#ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
if ((res = cris_request_io_interface(if_i2c, "I2C"))) {
|
||||
printk(KERN_CRIT "i2c_init: Failed to get IO interface\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
*R_PORT_PB_I2C = port_pb_i2c_shadow |=
|
||||
IO_STATE(R_PORT_PB_I2C, i2c_en, on) |
|
||||
IO_FIELD(R_PORT_PB_I2C, i2c_d, 1) |
|
||||
IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1) |
|
||||
IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable);
|
||||
|
||||
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0);
|
||||
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1);
|
||||
|
||||
*R_PORT_PB_DIR = (port_pb_dir_shadow |=
|
||||
IO_STATE(R_PORT_PB_DIR, dir0, input) |
|
||||
IO_STATE(R_PORT_PB_DIR, dir1, output));
|
||||
#else
|
||||
if ((res = cris_io_interface_allocate_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_DATA_PORT,
|
||||
CONFIG_ETRAX_I2C_DATA_PORT))) {
|
||||
printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n");
|
||||
return res;
|
||||
} else if ((res = cris_io_interface_allocate_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_CLK_PORT,
|
||||
CONFIG_ETRAX_I2C_CLK_PORT))) {
|
||||
cris_io_interface_free_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_DATA_PORT,
|
||||
CONFIG_ETRAX_I2C_DATA_PORT);
|
||||
printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init
|
||||
i2c_register(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = i2c_init();
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
|
||||
if(res < 0) {
|
||||
printk(KERN_ERR "i2c: couldn't get a major number.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this makes sure that i2c_register is called during boot */
|
||||
|
||||
module_init(i2c_register);
|
||||
|
||||
/****************** END OF FILE i2c.c ********************************/
|
17
arch/cris/arch-v10/drivers/i2c.h
Normal file
17
arch/cris/arch-v10/drivers/i2c.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* i2c.h */
|
||||
int i2c_init(void);
|
||||
|
||||
/* High level I2C actions */
|
||||
int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
|
||||
unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
|
||||
|
||||
/* Low level I2C */
|
||||
void i2c_start(void);
|
||||
void i2c_stop(void);
|
||||
void i2c_outbyte(unsigned char x);
|
||||
unsigned char i2c_inbyte(void);
|
||||
int i2c_getack(void);
|
||||
void i2c_sendack(void);
|
||||
|
||||
|
||||
|
1462
arch/cris/arch-v10/drivers/sync_serial.c
Normal file
1462
arch/cris/arch-v10/drivers/sync_serial.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue