Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
4b59d1b2
Commit
4b59d1b2
authored
May 30, 2002
by
Greg Kroah-Hartman
Browse files
Options
Browse Files
Download
Plain Diff
Merge kroah.com:/home/greg/linux/BK/bleeding_edge-2.5
into kroah.com:/home/greg/linux/BK/gregkh-2.5
parents
22775da0
86b56955
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
714 additions
and
590 deletions
+714
-590
drivers/usb/Config.in
drivers/usb/Config.in
+7
-1
drivers/usb/Makefile
drivers/usb/Makefile
+2
-0
drivers/usb/host/Config.in
drivers/usb/host/Config.in
+1
-0
drivers/usb/host/Makefile
drivers/usb/host/Makefile
+4
-1
drivers/usb/host/usb-ohci-pci.c
drivers/usb/host/usb-ohci-pci.c
+445
-0
drivers/usb/host/usb-ohci-sa1111.c
drivers/usb/host/usb-ohci-sa1111.c
+130
-0
drivers/usb/host/usb-ohci.c
drivers/usb/host/usb-ohci.c
+44
-434
drivers/usb/host/usb-ohci.h
drivers/usb/host/usb-ohci.h
+10
-5
drivers/usb/storage/freecom.c
drivers/usb/storage/freecom.c
+17
-116
drivers/usb/storage/scsiglue.c
drivers/usb/storage/scsiglue.c
+3
-3
drivers/usb/storage/transport.c
drivers/usb/storage/transport.c
+51
-30
No files found.
drivers/usb/Config.in
View file @
4b59d1b2
...
...
@@ -4,7 +4,13 @@
mainmenu_option next_comment
comment 'USB support'
dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" ]; then
tristate 'Support for USB' CONFIG_USB
else
define_bool CONFIG_USB n
fi
if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then
source drivers/usb/core/Config.in
...
...
drivers/usb/Makefile
View file @
4b59d1b2
...
...
@@ -6,9 +6,11 @@
mod-subdirs
:=
serial
obj-$(CONFIG_USB)
+=
core/
obj-$(CONFIG_USB_EHCI_HCD)
+=
host/
obj-$(CONFIG_USB_OHCI_HCD)
+=
host/
obj-$(CONFIG_USB_OHCI)
+=
host/
obj-$(CONFIG_USB_OHCI_SA1111)
+=
host/
obj-$(CONFIG_USB_SL811HS)
+=
host/
obj-$(CONFIG_USB_UHCI_ALT)
+=
host/
obj-$(CONFIG_USB_UHCI_HCD_ALT)
+=
host/
...
...
drivers/usb/host/Config.in
View file @
4b59d1b2
...
...
@@ -20,5 +20,6 @@ fi
#fi
#dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
if [ "$CONFIG_ARM" = "y" ]; then
dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB
dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB
fi
drivers/usb/host/Makefile
View file @
4b59d1b2
...
...
@@ -3,6 +3,8 @@
# framework and drivers
#
export-objs
:=
usb-ohci.o
obj-$(CONFIG_USB_EHCI_HCD)
+=
ehci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD)
+=
ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD)
+=
usb-uhci-hcd.o
...
...
@@ -10,7 +12,8 @@ obj-$(CONFIG_USB_UHCI_HCD_ALT) += uhci-hcd.o
obj-$(CONFIG_USB_UHCI)
+=
usb-uhci.o
obj-$(CONFIG_USB_UHCI_ALT)
+=
uhci.o
obj-$(CONFIG_USB_OHCI)
+=
usb-ohci.o
obj-$(CONFIG_USB_OHCI)
+=
usb-ohci.o usb-ohci-pci.o
obj-$(CONFIG_USB_OHCI_SA1111)
+=
usb-ohci.o usb-ohci-sa1111.o
obj-$(CONFIG_USB_SL811HS)
+=
hc_sl811.o
include
$(TOPDIR)/Rules.make
drivers/usb/host/usb-ohci-pci.c
0 → 100644
View file @
4b59d1b2
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
/* for in_interrupt() */
#undef DEBUG
#include <linux/usb.h>
#include "../core/hcd.h"
#include "usb-ohci.h"
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#ifndef CONFIG_PM
#define CONFIG_PM
#endif
#endif
/*-------------------------------------------------------------------------*/
/* Increment the module usage count, start the control thread and
* return success. */
static
struct
pci_driver
ohci_pci_driver
;
static
int
__devinit
hc_found_ohci
(
struct
pci_dev
*
dev
,
int
irq
,
void
*
mem_base
,
const
struct
pci_device_id
*
id
)
{
u8
latency
,
limit
;
ohci_t
*
ohci
;
int
ret
;
printk
(
KERN_INFO
__FILE__
": usb-%s, %s
\n
"
,
dev
->
slot_name
,
dev
->
name
);
/* bad pci latencies can contribute to overruns */
pci_read_config_byte
(
dev
,
PCI_LATENCY_TIMER
,
&
latency
);
if
(
latency
)
{
pci_read_config_byte
(
dev
,
PCI_MAX_LAT
,
&
limit
);
if
(
limit
&&
limit
<
latency
)
{
dbg
(
"PCI latency reduced to max %d"
,
limit
);
pci_write_config_byte
(
dev
,
PCI_LATENCY_TIMER
,
limit
);
latency
=
limit
;
}
}
ret
=
hc_add_ohci
(
dev
,
irq
,
mem_base
,
id
->
driver_data
,
&
ohci
,
ohci_pci_driver
.
name
,
dev
->
slot_name
);
if
(
ret
==
0
)
{
ohci
->
pci_latency
=
latency
;
if
(
hc_start
(
ohci
,
&
ohci
->
ohci_dev
->
dev
)
<
0
)
{
err
(
"can't start usb-%s"
,
ohci
->
slot_name
);
hc_remove_ohci
(
ohci
);
return
-
EBUSY
;
}
#ifdef DEBUG
ohci_dump
(
ohci
,
1
);
#endif
}
return
ret
;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
static
void
hc_restart
(
ohci_t
*
ohci
)
{
int
temp
;
int
i
;
if
(
ohci
->
pci_latency
)
pci_write_config_byte
(
ohci
->
ohci_dev
,
PCI_LATENCY_TIMER
,
ohci
->
pci_latency
);
ohci
->
disabled
=
1
;
ohci
->
sleeping
=
0
;
if
(
ohci
->
bus
->
root_hub
)
usb_disconnect
(
&
ohci
->
bus
->
root_hub
);
/* empty the interrupt branches */
for
(
i
=
0
;
i
<
NUM_INTS
;
i
++
)
ohci
->
ohci_int_load
[
i
]
=
0
;
for
(
i
=
0
;
i
<
NUM_INTS
;
i
++
)
ohci
->
hcca
->
int_table
[
i
]
=
0
;
/* no EDs to remove */
ohci
->
ed_rm_list
[
0
]
=
NULL
;
ohci
->
ed_rm_list
[
1
]
=
NULL
;
/* empty control and bulk lists */
ohci
->
ed_isotail
=
NULL
;
ohci
->
ed_controltail
=
NULL
;
ohci
->
ed_bulktail
=
NULL
;
if
((
temp
=
hc_reset
(
ohci
))
<
0
||
(
temp
=
hc_start
(
ohci
))
<
0
)
{
err
(
"can't restart usb-%s, %d"
,
ohci
->
ohci_dev
->
slot_name
,
temp
);
}
else
dbg
(
"restart usb-%s completed"
,
ohci
->
ohci_dev
->
slot_name
);
}
#endif
/* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* configured so that an OHCI device is always provided */
/* always called with process context; sleeping is OK */
static
int
__devinit
ohci_pci_probe
(
struct
pci_dev
*
dev
,
const
struct
pci_device_id
*
id
)
{
unsigned
long
mem_resource
,
mem_len
;
void
*
mem_base
;
int
status
;
if
(
pci_enable_device
(
dev
)
<
0
)
return
-
ENODEV
;
if
(
!
dev
->
irq
)
{
err
(
"found OHCI device with no IRQ assigned. check BIOS settings!"
);
pci_disable_device
(
dev
);
return
-
ENODEV
;
}
/* we read its hardware registers as memory */
mem_resource
=
pci_resource_start
(
dev
,
0
);
mem_len
=
pci_resource_len
(
dev
,
0
);
if
(
!
request_mem_region
(
mem_resource
,
mem_len
,
ohci_pci_driver
.
name
))
{
dbg
(
"controller already in use"
);
pci_disable_device
(
dev
);
return
-
EBUSY
;
}
mem_base
=
ioremap_nocache
(
mem_resource
,
mem_len
);
if
(
!
mem_base
)
{
err
(
"Error mapping OHCI memory"
);
release_mem_region
(
mem_resource
,
mem_len
);
pci_disable_device
(
dev
);
return
-
EFAULT
;
}
/* controller writes into our memory */
pci_set_master
(
dev
);
status
=
hc_found_ohci
(
dev
,
dev
->
irq
,
mem_base
,
id
);
if
(
status
<
0
)
{
iounmap
(
mem_base
);
release_mem_region
(
mem_resource
,
mem_len
);
pci_disable_device
(
dev
);
}
return
status
;
}
/*-------------------------------------------------------------------------*/
/* may be called from interrupt context [interface spec] */
/* may be called without controller present */
/* may be called with controller, bus, and devices active */
static
void
__devexit
ohci_pci_remove
(
struct
pci_dev
*
dev
)
{
ohci_t
*
ohci
=
(
ohci_t
*
)
pci_get_drvdata
(
dev
);
dbg
(
"remove %s controller usb-%s%s%s"
,
hcfs2string
(
ohci
->
hc_control
&
OHCI_CTRL_HCFS
),
dev
->
slot_name
,
ohci
->
disabled
?
" (disabled)"
:
""
,
in_interrupt
()
?
" in interrupt"
:
""
);
hc_remove_ohci
(
ohci
);
release_mem_region
(
pci_resource_start
(
dev
,
0
),
pci_resource_len
(
dev
,
0
));
}
#ifdef CONFIG_PM
/*-------------------------------------------------------------------------*/
static
int
ohci_pci_suspend
(
struct
pci_dev
*
dev
,
u32
state
)
{
ohci_t
*
ohci
=
(
ohci_t
*
)
pci_get_drvdata
(
dev
);
unsigned
long
flags
;
u16
cmd
;
if
((
ohci
->
hc_control
&
OHCI_CTRL_HCFS
)
!=
OHCI_USB_OPER
)
{
dbg
(
"can't suspend usb-%s (state is %s)"
,
dev
->
slot_name
,
hcfs2string
(
ohci
->
hc_control
&
OHCI_CTRL_HCFS
));
return
-
EIO
;
}
/* act as if usb suspend can always be used */
info
(
"USB suspend: usb-%s"
,
dev
->
slot_name
);
ohci
->
sleeping
=
1
;
/* First stop processing */
spin_lock_irqsave
(
&
usb_ed_lock
,
flags
);
ohci
->
hc_control
&=
~
(
OHCI_CTRL_PLE
|
OHCI_CTRL_CLE
|
OHCI_CTRL_BLE
|
OHCI_CTRL_IE
);
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrstatus
);
(
void
)
readl
(
&
ohci
->
regs
->
intrstatus
);
spin_unlock_irqrestore
(
&
usb_ed_lock
,
flags
);
/* Wait a frame or two */
mdelay
(
1
);
if
(
!
readl
(
&
ohci
->
regs
->
intrstatus
)
&
OHCI_INTR_SF
)
mdelay
(
1
);
#ifdef CONFIG_PMAC_PBOOK
if
(
_machine
==
_MACH_Pmac
)
disable_irq
(
ohci
->
irq
);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
/* Enable remote wakeup */
writel
(
readl
(
&
ohci
->
regs
->
intrenable
)
|
OHCI_INTR_RD
,
&
ohci
->
regs
->
intrenable
);
/* Suspend chip and let things settle down a bit */
ohci
->
hc_control
=
OHCI_USB_SUSPEND
;
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
500
);
/* No schedule here ! */
switch
(
readl
(
&
ohci
->
regs
->
control
)
&
OHCI_CTRL_HCFS
)
{
case
OHCI_USB_RESET
:
dbg
(
"Bus in reset phase ???"
);
break
;
case
OHCI_USB_RESUME
:
dbg
(
"Bus in resume phase ???"
);
break
;
case
OHCI_USB_OPER
:
dbg
(
"Bus in operational phase ???"
);
break
;
case
OHCI_USB_SUSPEND
:
dbg
(
"Bus suspended"
);
break
;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable it's bus master bit during
* suspend
*/
pci_read_config_word
(
dev
,
PCI_COMMAND
,
&
cmd
);
cmd
&=
~
PCI_COMMAND_MASTER
;
pci_write_config_word
(
dev
,
PCI_COMMAND
,
cmd
);
#ifdef CONFIG_PMAC_PBOOK
{
struct
device_node
*
of_node
;
/* Disable USB PAD & cell clock */
of_node
=
pci_device_to_OF_node
(
ohci
->
ohci_dev
);
if
(
of_node
&&
_machine
==
_MACH_Pmac
)
pmac_call_feature
(
PMAC_FTR_USB_ENABLE
,
of_node
,
0
,
0
);
}
#endif
return
0
;
}
/*-------------------------------------------------------------------------*/
static
int
ohci_pci_resume
(
struct
pci_dev
*
dev
)
{
ohci_t
*
ohci
=
(
ohci_t
*
)
pci_get_drvdata
(
dev
);
int
temp
;
unsigned
long
flags
;
/* guard against multiple resumes */
atomic_inc
(
&
ohci
->
resume_count
);
if
(
atomic_read
(
&
ohci
->
resume_count
)
!=
1
)
{
err
(
"concurrent PCI resumes for usb-%s"
,
dev
->
slot_name
);
atomic_dec
(
&
ohci
->
resume_count
);
return
0
;
}
#ifdef CONFIG_PMAC_PBOOK
{
struct
device_node
*
of_node
;
/* Re-enable USB PAD & cell clock */
of_node
=
pci_device_to_OF_node
(
ohci
->
ohci_dev
);
if
(
of_node
&&
_machine
==
_MACH_Pmac
)
pmac_call_feature
(
PMAC_FTR_USB_ENABLE
,
of_node
,
0
,
1
);
}
#endif
/* did we suspend, or were we powered off? */
ohci
->
hc_control
=
readl
(
&
ohci
->
regs
->
control
);
temp
=
ohci
->
hc_control
&
OHCI_CTRL_HCFS
;
#ifdef DEBUG
/* the registers may look crazy here */
ohci_dump_status
(
ohci
);
#endif
/* Re-enable bus mastering */
pci_set_master
(
ohci
->
ohci_dev
);
switch
(
temp
)
{
case
OHCI_USB_RESET
:
// lost power
info
(
"USB restart: usb-%s"
,
dev
->
slot_name
);
hc_restart
(
ohci
);
break
;
case
OHCI_USB_SUSPEND
:
// host wakeup
case
OHCI_USB_RESUME
:
// remote wakeup
info
(
"USB continue: usb-%s from %s wakeup"
,
dev
->
slot_name
,
(
temp
==
OHCI_USB_SUSPEND
)
?
"host"
:
"remote"
);
ohci
->
hc_control
=
OHCI_USB_RESUME
;
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
20
);
/* no schedule here ! */
/* Some controllers (lucent) need a longer delay here */
mdelay
(
15
);
temp
=
readl
(
&
ohci
->
regs
->
control
);
temp
=
ohci
->
hc_control
&
OHCI_CTRL_HCFS
;
if
(
temp
!=
OHCI_USB_RESUME
)
{
err
(
"controller usb-%s won't resume"
,
dev
->
slot_name
);
ohci
->
disabled
=
1
;
return
-
EIO
;
}
/* Some chips likes being resumed first */
writel
(
OHCI_USB_OPER
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
3
);
/* Then re-enable operations */
spin_lock_irqsave
(
&
usb_ed_lock
,
flags
);
ohci
->
disabled
=
0
;
ohci
->
sleeping
=
0
;
ohci
->
hc_control
=
OHCI_CONTROL_INIT
|
OHCI_USB_OPER
;
if
(
!
ohci
->
ed_rm_list
[
0
]
&&
!
ohci
->
ed_rm_list
[
1
])
{
if
(
ohci
->
ed_controltail
)
ohci
->
hc_control
|=
OHCI_CTRL_CLE
;
if
(
ohci
->
ed_bulktail
)
ohci
->
hc_control
|=
OHCI_CTRL_BLE
;
}
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrstatus
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrenable
);
/* Check for a pending done list */
writel
(
OHCI_INTR_WDH
,
&
ohci
->
regs
->
intrdisable
);
(
void
)
readl
(
&
ohci
->
regs
->
intrdisable
);
spin_unlock_irqrestore
(
&
usb_ed_lock
,
flags
);
#ifdef CONFIG_PMAC_PBOOK
if
(
_machine
==
_MACH_Pmac
)
enable_irq
(
ohci
->
irq
);
#endif
if
(
ohci
->
hcca
->
done_head
)
dl_done_list
(
ohci
,
dl_reverse_done_list
(
ohci
));
writel
(
OHCI_INTR_WDH
,
&
ohci
->
regs
->
intrenable
);
writel
(
OHCI_BLF
,
&
ohci
->
regs
->
cmdstatus
);
/* start bulk list */
writel
(
OHCI_CLF
,
&
ohci
->
regs
->
cmdstatus
);
/* start Control list */
break
;
default:
warn
(
"odd PCI resume for usb-%s"
,
dev
->
slot_name
);
}
/* controller is operational, extra resumes are harmless */
atomic_dec
(
&
ohci
->
resume_count
);
return
0
;
}
#endif
/* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static
const
struct
pci_device_id
__devinitdata
ohci_pci_ids
[]
=
{
{
/*
* AMD-756 [Viper] USB has a serious erratum when used with
* lowspeed devices like mice.
*/
vendor:
0x1022
,
device:
0x740c
,
subvendor:
PCI_ANY_ID
,
subdevice:
PCI_ANY_ID
,
driver_data:
OHCI_QUIRK_AMD756
,
}
,
{
/* handle any USB OHCI controller */
class:
((
PCI_CLASS_SERIAL_USB
<<
8
)
|
0x10
),
class_mask:
~
0
,
/* no matter who makes it */
vendor:
PCI_ANY_ID
,
device:
PCI_ANY_ID
,
subvendor:
PCI_ANY_ID
,
subdevice:
PCI_ANY_ID
,
},
{
/* end: all zeroes */
}
};
MODULE_DEVICE_TABLE
(
pci
,
ohci_pci_ids
);
static
struct
pci_driver
ohci_pci_driver
=
{
name:
"usb-ohci"
,
id_table:
&
ohci_pci_ids
[
0
],
probe:
ohci_pci_probe
,
remove:
__devexit_p
(
ohci_pci_remove
),
#ifdef CONFIG_PM
suspend:
ohci_pci_suspend
,
resume:
ohci_pci_resume
,
#endif
/* PM */
};
/*-------------------------------------------------------------------------*/
static
int
__init
ohci_hcd_init
(
void
)
{
return
pci_module_init
(
&
ohci_pci_driver
);
}
/*-------------------------------------------------------------------------*/
static
void
__exit
ohci_hcd_cleanup
(
void
)
{
pci_unregister_driver
(
&
ohci_pci_driver
);
}
module_init
(
ohci_hcd_init
);
module_exit
(
ohci_hcd_cleanup
);
MODULE_LICENSE
(
"GPL"
);
drivers/usb/host/usb-ohci-sa1111.c
0 → 100644
View file @
4b59d1b2
/*
* linux/drivers/usb/usb-ohci-sa1111.c
*
* The outline of this code was taken from Brad Parkers <brad@heeltoe.com>
* original OHCI driver modifications, and reworked into a cleaner form
* by Russell King <rmk@arm.linux.org.uk>.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/assabet.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#include "usb-ohci.h"
int
__devinit
hc_add_ohci
(
struct
pci_dev
*
dev
,
int
irq
,
void
*
membase
,
unsigned
long
flags
,
ohci_t
**
ohci
,
const
char
*
name
,
const
char
*
slot_name
);
extern
void
hc_remove_ohci
(
ohci_t
*
ohci
);
static
ohci_t
*
sa1111_ohci
;
static
void
__init
sa1111_ohci_configure
(
void
)
{
unsigned
int
usb_rst
=
0
;
if
(
machine_is_xp860
()
||
machine_has_neponset
()
||
machine_is_pfs168
()
||
machine_is_badge4
())
usb_rst
=
USB_RESET_PWRSENSELOW
|
USB_RESET_PWRCTRLLOW
;
/*
* Configure the power sense and control lines. Place the USB
* host controller in reset.
*/
USB_RESET
=
usb_rst
|
USB_RESET_FORCEIFRESET
|
USB_RESET_FORCEHCRESET
;
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
SKPCR
|=
SKPCR_UCLKEN
;
udelay
(
11
);
USB_RESET
=
usb_rst
;
}
static
int
__init
sa1111_ohci_init
(
void
)
{
int
ret
;
if
(
!
sa1111
)
return
-
ENODEV
;
/*
* Request memory resources.
*/
// if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci"))
// return -EBUSY;
sa1111_ohci_configure
();
/*
* Initialise the generic OHCI driver.
*/
ret
=
hc_add_ohci
((
struct
pci_dev
*
)
1
,
NIRQHCIM
,
(
void
*
)
&
USB_OHCI_OP_BASE
,
0
,
&
sa1111_ohci
,
"usb-ohci"
,
"sa1111"
);
if
(
ret
==
0
)
{
if
(
hc_start
(
sa1111_ohci
,
&
sa1111
->
dev
)
<
0
)
{
err
(
"can't start usb-%s"
,
sa1111_ohci
->
slot_name
);
hc_remove_ohci
(
sa1111_ohci
);
return
-
EBUSY
;
}
#ifdef DEBUG
ohci_dump
(
ohci
,
1
);
#endif
#ifdef CONFIG_SA1100_BADGE4
if
(
machine_is_badge4
())
{
/* found the controller, so now power the bus */
badge4_set_5V
(
BADGE4_5V_USB
,
1
);
}
#endif
}
// else
// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return
ret
;
}
static
void
__exit
sa1111_ohci_exit
(
void
)
{
hc_remove_ohci
(
sa1111_ohci
);
/*
* Put the USB host controller into reset.
*/
USB_RESET
|=
USB_RESET_FORCEIFRESET
|
USB_RESET_FORCEHCRESET
;
/*
* Stop the USB clock.
*/
SKPCR
&=
~
SKPCR_UCLKEN
;
/*
* Release memory resources.
*/
// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
#ifdef CONFIG_SA1100_BADGE4
if
(
machine_is_badge4
())
{
badge4_set_5V
(
BADGE4_5V_USB
,
0
);
}
#endif
}
module_init
(
sa1111_ohci_init
);
module_exit
(
sa1111_ohci_exit
);
drivers/usb/host/usb-ohci.c
View file @
4b59d1b2
...
...
@@ -82,16 +82,6 @@
#include "usb-ohci.h"
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#ifndef CONFIG_PM
#define CONFIG_PM
#endif
#endif
/*
* Version Information
*/
...
...
@@ -99,14 +89,10 @@
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB OHCI Host Controller Driver"
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
#define OHCI_UNLINK_TIMEOUT (HZ / 10)
static
LIST_HEAD
(
ohci_hcd_list
);
s
tatic
s
pinlock_t
usb_ed_lock
=
SPIN_LOCK_UNLOCKED
;
spinlock_t
usb_ed_lock
=
SPIN_LOCK_UNLOCKED
;
/*-------------------------------------------------------------------------*/
...
...
@@ -443,7 +429,7 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose)
static
void
ohci_dump
(
ohci_t
*
controller
,
int
verbose
)
{
dbg
(
"OHCI controller usb-%s state"
,
controller
->
ohci_dev
->
slot_name
);
dbg
(
"OHCI controller usb-%s state"
,
controller
->
slot_name
);
// dumps some of the state we know about
ohci_dump_status
(
controller
);
...
...
@@ -465,7 +451,7 @@ static void ohci_dump (ohci_t *controller, int verbose)
static
int
sohci_return_urb
(
struct
ohci
*
hc
,
struct
urb
*
urb
)
{
urb_priv_t
*
urb_priv
=
urb
->
hcpriv
;
struct
urb
*
urbt
;
struct
urb
*
urbt
=
NULL
;
unsigned
long
flags
;
int
i
;
...
...
@@ -499,7 +485,7 @@ static int sohci_return_urb (struct ohci *hc, struct urb * urb)
break
;
case
PIPE_ISOCHRONOUS
:
for
(
urbt
=
urb
->
next
;
urbt
&&
(
urbt
!=
urb
);
urbt
=
urbt
->
next
);
//
for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
if
(
urbt
)
{
/* send the reply and requeue URB */
pci_unmap_single
(
hc
->
ohci_dev
,
urb_priv
->
td
[
0
]
->
data_dma
,
...
...
@@ -865,7 +851,7 @@ static int sohci_free_dev (struct usb_device * usb_dev)
if
(
ed
->
state
==
ED_OPER
)
{
/* driver on that interface didn't unlink an urb */
dbg
(
"driver usb-%s dev %d ed 0x%x unfreed URB"
,
ohci
->
ohci_dev
->
slot_name
,
usb_dev
->
devnum
,
i
);
ohci
->
slot_name
,
usb_dev
->
devnum
,
i
);
ep_unlink
(
ohci
,
ed
);
}
ep_rm_ed
(
usb_dev
,
ed
);
...
...
@@ -910,7 +896,7 @@ static int sohci_free_dev (struct usb_device * usb_dev)
}
else
{
/* likely some interface's driver has a refcount bug */
err
(
"bus %s devnum %d deletion in interrupt"
,
ohci
->
ohci_dev
->
slot_name
,
usb_dev
->
devnum
);
ohci
->
slot_name
,
usb_dev
->
devnum
);
BUG
();
}
}
...
...
@@ -1529,7 +1515,7 @@ static void dl_del_urb (struct urb * urb)
/* replies to the request have to be on a FIFO basis so
* we reverse the reversed done-list */
static
td_t
*
dl_reverse_done_list
(
ohci_t
*
ohci
)
td_t
*
dl_reverse_done_list
(
ohci_t
*
ohci
)
{
__u32
td_list_hc
;
td_t
*
td_rev
=
NULL
;
...
...
@@ -1677,7 +1663,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame)
/* td done list */
static
void
dl_done_list
(
ohci_t
*
ohci
,
td_t
*
td_list
)
void
dl_done_list
(
ohci_t
*
ohci
,
td_t
*
td_list
)
{
td_t
*
td_list_next
=
NULL
;
ed_t
*
ed
;
...
...
@@ -1822,7 +1808,7 @@ static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
num_ports
=
roothub_a
(
ohci
)
&
RH_A_NDP
;
if
(
num_ports
>
MAX_ROOT_PORTS
)
{
err
(
"bogus NDP=%d for OHCI usb-%s"
,
num_ports
,
ohci
->
ohci_dev
->
slot_name
);
ohci
->
slot_name
);
err
(
"rereads as NDP=%d"
,
readl
(
&
ohci
->
regs
->
roothub
.
a
)
&
RH_A_NDP
);
/* retry later; "should not happen" */
...
...
@@ -2148,7 +2134,7 @@ static int rh_unlink_urb (struct urb * urb)
/* reset the HC and BUS */
static
int
hc_reset
(
ohci_t
*
ohci
)
int
hc_reset
(
ohci_t
*
ohci
)
{
int
timeout
=
30
;
int
smm_timeout
=
50
;
/* 0,5 sec */
...
...
@@ -2169,7 +2155,7 @@ static int hc_reset (ohci_t * ohci)
writel
(
OHCI_INTR_MIE
,
&
ohci
->
regs
->
intrdisable
);
dbg
(
"USB HC reset_hc usb-%s: ctrl = 0x%x ;"
,
ohci
->
ohci_dev
->
slot_name
,
ohci
->
slot_name
,
readl
(
&
ohci
->
regs
->
control
));
/* Reset USB (needed by some controllers) */
...
...
@@ -2193,7 +2179,7 @@ static int hc_reset (ohci_t * ohci)
* enable interrupts
* connect the virtual root hub */
static
int
hc_start
(
ohci_t
*
ohci
)
int
hc_start
(
ohci_t
*
ohci
,
struct
device
*
parent_dev
)
{
__u32
mask
;
unsigned
int
fminterval
;
...
...
@@ -2247,7 +2233,7 @@ static int hc_start (ohci_t * ohci)
dev
=
usb_to_ohci
(
usb_dev
);
ohci
->
bus
->
root_hub
=
usb_dev
;
usb_connect
(
usb_dev
);
if
(
usb_register_root_hub
(
usb_dev
,
&
ohci
->
ohci_dev
->
dev
)
!=
0
)
{
if
(
usb_register_root_hub
(
usb_dev
,
parent_
dev
)
!=
0
)
{
usb_free_dev
(
usb_dev
);
ohci
->
disabled
=
1
;
return
-
ENODEV
;
...
...
@@ -2308,7 +2294,7 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
if
(
ints
&
OHCI_INTR_UE
)
{
ohci
->
disabled
++
;
err
(
"OHCI Unrecoverable Error, controller usb-%s disabled"
,
ohci
->
ohci_dev
->
slot_name
);
ohci
->
slot_name
);
// e.g. due to PCI Master/Target Abort
#ifdef DEBUG
...
...
@@ -2385,7 +2371,9 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
ohci
->
regs
=
mem_base
;
ohci
->
ohci_dev
=
dev
;
#ifdef CONFIG_PCI
pci_set_drvdata
(
dev
,
ohci
);
#endif
INIT_LIST_HEAD
(
&
ohci
->
ohci_hcd_list
);
list_add
(
&
ohci
->
ohci_hcd_list
,
&
ohci_hcd_list
);
...
...
@@ -2394,7 +2382,9 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
ohci
->
bus
=
usb_alloc_bus
(
&
sohci_device_operations
);
if
(
!
ohci
->
bus
)
{
#ifdef CONFIG_PCI
pci_set_drvdata
(
dev
,
NULL
);
#endif
pci_free_consistent
(
ohci
->
ohci_dev
,
sizeof
*
ohci
->
hcca
,
ohci
->
hcca
,
ohci
->
hcca_dma
);
kfree
(
ohci
);
...
...
@@ -2413,7 +2403,7 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
static
void
hc_release_ohci
(
ohci_t
*
ohci
)
{
dbg
(
"USB HC release ohci usb-%s"
,
ohci
->
ohci_dev
->
slot_name
);
dbg
(
"USB HC release ohci usb-%s"
,
ohci
->
slot_name
);
/* disconnect all devices */
if
(
ohci
->
bus
->
root_hub
)
...
...
@@ -2426,7 +2416,9 @@ static void hc_release_ohci (ohci_t * ohci)
free_irq
(
ohci
->
irq
,
ohci
);
ohci
->
irq
=
-
1
;
}
#ifdef CONFIG_PCI
pci_set_drvdata
(
ohci
->
ohci_dev
,
NULL
);
#endif
if
(
ohci
->
bus
)
{
if
(
ohci
->
bus
->
busnum
)
usb_deregister_bus
(
ohci
->
bus
);
...
...
@@ -2448,18 +2440,15 @@ static void hc_release_ohci (ohci_t * ohci)
/*-------------------------------------------------------------------------*/
/* Increment the module usage count, start the control thread and
* return success. */
static
struct
pci_driver
ohci_pci_driver
;
static
int
__devinit
hc_found_ohci
(
struct
pci_dev
*
dev
,
int
irq
,
void
*
mem_base
,
const
struct
pci_device_id
*
id
)
/*
* Host bus independent add one OHCI host controller.
*/
int
hc_add_ohci
(
struct
pci_dev
*
dev
,
int
irq
,
void
*
mem_base
,
unsigned
long
flags
,
ohci_t
**
ohcip
,
const
char
*
name
,
const
char
*
slot_name
)
{
ohci_t
*
ohci
;
u8
latency
,
limit
;
char
buf
[
8
],
*
bufp
=
buf
;
ohci_t
*
ohci
;
int
ret
;
#ifndef __sparc__
...
...
@@ -2469,34 +2458,20 @@ hc_found_ohci (struct pci_dev *dev, int irq,
#endif
printk
(
KERN_INFO
__FILE__
": USB OHCI at membase 0x%lx, IRQ %s
\n
"
,
(
unsigned
long
)
mem_base
,
bufp
);
printk
(
KERN_INFO
__FILE__
": usb-%s, %s
\n
"
,
dev
->
slot_name
,
dev
->
name
);
ohci
=
hc_alloc_ohci
(
dev
,
mem_base
);
if
(
!
ohci
)
{
return
-
ENOMEM
;
}
ohci
->
slot_name
=
slot_name
;
if
((
ret
=
ohci_mem_init
(
ohci
))
<
0
)
{
hc_release_ohci
(
ohci
);
return
ret
;
}
ohci
->
flags
=
id
->
driver_data
;
ohci
->
flags
=
flags
;
if
(
ohci
->
flags
&
OHCI_QUIRK_AMD756
)
printk
(
KERN_INFO
__FILE__
": AMD756 erratum 4 workaround
\n
"
);
/* bad pci latencies can contribute to overruns */
pci_read_config_byte
(
dev
,
PCI_LATENCY_TIMER
,
&
latency
);
if
(
latency
)
{
pci_read_config_byte
(
dev
,
PCI_MAX_LAT
,
&
limit
);
if
(
limit
&&
limit
<
latency
)
{
dbg
(
"PCI latency reduced to max %d"
,
limit
);
pci_write_config_byte
(
dev
,
PCI_LATENCY_TIMER
,
limit
);
ohci
->
pci_latency
=
limit
;
}
else
{
/* it might already have been reduced */
ohci
->
pci_latency
=
latency
;
}
}
if
(
hc_reset
(
ohci
)
<
0
)
{
hc_release_ohci
(
ohci
);
return
-
ENODEV
;
...
...
@@ -2508,134 +2483,23 @@ hc_found_ohci (struct pci_dev *dev, int irq,
usb_register_bus
(
ohci
->
bus
);
if
(
request_irq
(
irq
,
hc_interrupt
,
SA_SHIRQ
,
ohci_pci_driver
.
name
,
ohci
)
!=
0
)
{
if
(
request_irq
(
irq
,
hc_interrupt
,
SA_SHIRQ
,
name
,
ohci
)
!=
0
)
{
err
(
"request interrupt %s failed"
,
bufp
);
hc_release_ohci
(
ohci
);
return
-
EBUSY
;
}
ohci
->
irq
=
irq
;
if
(
hc_start
(
ohci
)
<
0
)
{
err
(
"can't start usb-%s"
,
dev
->
slot_name
);
hc_release_ohci
(
ohci
);
return
-
EBUSY
;
}
*
ohcip
=
ohci
;
#ifdef DEBUG
ohci_dump
(
ohci
,
1
);
#endif
return
0
;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
static
void
hc_restart
(
ohci_t
*
ohci
)
{
int
temp
;
int
i
;
if
(
ohci
->
pci_latency
)
pci_write_config_byte
(
ohci
->
ohci_dev
,
PCI_LATENCY_TIMER
,
ohci
->
pci_latency
);
ohci
->
disabled
=
1
;
ohci
->
sleeping
=
0
;
if
(
ohci
->
bus
->
root_hub
)
usb_disconnect
(
&
ohci
->
bus
->
root_hub
);
/* empty the interrupt branches */
for
(
i
=
0
;
i
<
NUM_INTS
;
i
++
)
ohci
->
ohci_int_load
[
i
]
=
0
;
for
(
i
=
0
;
i
<
NUM_INTS
;
i
++
)
ohci
->
hcca
->
int_table
[
i
]
=
0
;
/* no EDs to remove */
ohci
->
ed_rm_list
[
0
]
=
NULL
;
ohci
->
ed_rm_list
[
1
]
=
NULL
;
/* empty control and bulk lists */
ohci
->
ed_isotail
=
NULL
;
ohci
->
ed_controltail
=
NULL
;
ohci
->
ed_bulktail
=
NULL
;
if
((
temp
=
hc_reset
(
ohci
))
<
0
||
(
temp
=
hc_start
(
ohci
))
<
0
)
{
err
(
"can't restart usb-%s, %d"
,
ohci
->
ohci_dev
->
slot_name
,
temp
);
}
else
dbg
(
"restart usb-%s completed"
,
ohci
->
ohci_dev
->
slot_name
);
}
#endif
/* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* configured so that an OHCI device is always provided */
/* always called with process context; sleeping is OK */
static
int
__devinit
ohci_pci_probe
(
struct
pci_dev
*
dev
,
const
struct
pci_device_id
*
id
)
{
unsigned
long
mem_resource
,
mem_len
;
void
*
mem_base
;
int
status
;
if
(
pci_enable_device
(
dev
)
<
0
)
return
-
ENODEV
;
if
(
!
dev
->
irq
)
{
err
(
"found OHCI device with no IRQ assigned. check BIOS settings!"
);
pci_disable_device
(
dev
);
return
-
ENODEV
;
}
/* we read its hardware registers as memory */
mem_resource
=
pci_resource_start
(
dev
,
0
);
mem_len
=
pci_resource_len
(
dev
,
0
);
if
(
!
request_mem_region
(
mem_resource
,
mem_len
,
ohci_pci_driver
.
name
))
{
dbg
(
"controller already in use"
);
pci_disable_device
(
dev
);
return
-
EBUSY
;
}
mem_base
=
ioremap_nocache
(
mem_resource
,
mem_len
);
if
(
!
mem_base
)
{
err
(
"Error mapping OHCI memory"
);
release_mem_region
(
mem_resource
,
mem_len
);
pci_disable_device
(
dev
);
return
-
EFAULT
;
}
/* controller writes into our memory */
pci_set_master
(
dev
);
status
=
hc_found_ohci
(
dev
,
dev
->
irq
,
mem_base
,
id
);
if
(
status
<
0
)
{
iounmap
(
mem_base
);
release_mem_region
(
mem_resource
,
mem_len
);
pci_disable_device
(
dev
);
}
return
status
;
}
/*-------------------------------------------------------------------------*/
/* may be called from interrupt context [interface spec] */
/* may be called without controller present */
/* may be called with controller, bus, and devices active */
static
void
__devexit
ohci_pci_remove
(
struct
pci_dev
*
dev
)
/*
* Host bus independent remove one OHCI host controller.
*/
void
hc_remove_ohci
(
ohci_t
*
ohci
)
{
ohci_t
*
ohci
=
pci_get_drvdata
(
dev
);
dbg
(
"remove %s controller usb-%s%s%s"
,
hcfs2string
(
ohci
->
hc_control
&
OHCI_CTRL_HCFS
),
dev
->
slot_name
,
ohci
->
disabled
?
" (disabled)"
:
""
,
in_interrupt
()
?
" in interrupt"
:
""
);
#ifdef DEBUG
ohci_dump
(
ohci
,
1
);
#endif
...
...
@@ -2652,270 +2516,16 @@ ohci_pci_remove (struct pci_dev *dev)
&
ohci
->
regs
->
control
);
hc_release_ohci
(
ohci
);
release_mem_region
(
pci_resource_start
(
dev
,
0
),
pci_resource_len
(
dev
,
0
));
pci_disable_device
(
dev
);
}
#ifdef CONFIG_PM
/*-------------------------------------------------------------------------*/
static
int
ohci_pci_suspend
(
struct
pci_dev
*
dev
,
u32
state
)
{
ohci_t
*
ohci
=
pci_get_drvdata
(
dev
);
unsigned
long
flags
;
u16
cmd
;
if
((
ohci
->
hc_control
&
OHCI_CTRL_HCFS
)
!=
OHCI_USB_OPER
)
{
dbg
(
"can't suspend usb-%s (state is %s)"
,
dev
->
slot_name
,
hcfs2string
(
ohci
->
hc_control
&
OHCI_CTRL_HCFS
));
return
-
EIO
;
}
/* act as if usb suspend can always be used */
info
(
"USB suspend: usb-%s"
,
dev
->
slot_name
);
ohci
->
sleeping
=
1
;
/* First stop processing */
spin_lock_irqsave
(
&
usb_ed_lock
,
flags
);
ohci
->
hc_control
&=
~
(
OHCI_CTRL_PLE
|
OHCI_CTRL_CLE
|
OHCI_CTRL_BLE
|
OHCI_CTRL_IE
);
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrstatus
);
(
void
)
readl
(
&
ohci
->
regs
->
intrstatus
);
spin_unlock_irqrestore
(
&
usb_ed_lock
,
flags
);
/* Wait a frame or two */
mdelay
(
1
);
if
(
!
readl
(
&
ohci
->
regs
->
intrstatus
)
&
OHCI_INTR_SF
)
mdelay
(
1
);
#ifdef CONFIG_PMAC_PBOOK
if
(
_machine
==
_MACH_Pmac
)
disable_irq
(
ohci
->
irq
);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
/* Enable remote wakeup */
writel
(
readl
(
&
ohci
->
regs
->
intrenable
)
|
OHCI_INTR_RD
,
&
ohci
->
regs
->
intrenable
);
/* Suspend chip and let things settle down a bit */
ohci
->
hc_control
=
OHCI_USB_SUSPEND
;
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
500
);
/* No schedule here ! */
switch
(
readl
(
&
ohci
->
regs
->
control
)
&
OHCI_CTRL_HCFS
)
{
case
OHCI_USB_RESET
:
dbg
(
"Bus in reset phase ???"
);
break
;
case
OHCI_USB_RESUME
:
dbg
(
"Bus in resume phase ???"
);
break
;
case
OHCI_USB_OPER
:
dbg
(
"Bus in operational phase ???"
);
break
;
case
OHCI_USB_SUSPEND
:
dbg
(
"Bus suspended"
);
break
;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable it's bus master bit during
* suspend
*/
pci_read_config_word
(
dev
,
PCI_COMMAND
,
&
cmd
);
cmd
&=
~
PCI_COMMAND_MASTER
;
pci_write_config_word
(
dev
,
PCI_COMMAND
,
cmd
);
#ifdef CONFIG_PMAC_PBOOK
{
struct
device_node
*
of_node
;
/* Disable USB PAD & cell clock */
of_node
=
pci_device_to_OF_node
(
ohci
->
ohci_dev
);
if
(
of_node
)
pmac_call_feature
(
PMAC_FTR_USB_ENABLE
,
of_node
,
0
,
0
);
}
#endif
return
0
;
}
/*-------------------------------------------------------------------------*/
static
int
ohci_pci_resume
(
struct
pci_dev
*
dev
)
{
ohci_t
*
ohci
=
pci_get_drvdata
(
dev
);
int
temp
;
unsigned
long
flags
;
/* guard against multiple resumes */
atomic_inc
(
&
ohci
->
resume_count
);
if
(
atomic_read
(
&
ohci
->
resume_count
)
!=
1
)
{
err
(
"concurrent PCI resumes for usb-%s"
,
dev
->
slot_name
);
atomic_dec
(
&
ohci
->
resume_count
);
return
0
;
}
#ifdef CONFIG_PMAC_PBOOK
{
struct
device_node
*
of_node
;
/* Re-enable USB PAD & cell clock */
of_node
=
pci_device_to_OF_node
(
ohci
->
ohci_dev
);
if
(
of_node
)
pmac_call_feature
(
PMAC_FTR_USB_ENABLE
,
of_node
,
0
,
1
);
}
#endif
/* did we suspend, or were we powered off? */
ohci
->
hc_control
=
readl
(
&
ohci
->
regs
->
control
);
temp
=
ohci
->
hc_control
&
OHCI_CTRL_HCFS
;
#ifdef DEBUG
/* the registers may look crazy here */
ohci_dump_status
(
ohci
);
#endif
/* Re-enable bus mastering */
pci_set_master
(
ohci
->
ohci_dev
);
switch
(
temp
)
{
case
OHCI_USB_RESET
:
// lost power
info
(
"USB restart: usb-%s"
,
dev
->
slot_name
);
hc_restart
(
ohci
);
break
;
case
OHCI_USB_SUSPEND
:
// host wakeup
case
OHCI_USB_RESUME
:
// remote wakeup
info
(
"USB continue: usb-%s from %s wakeup"
,
dev
->
slot_name
,
(
temp
==
OHCI_USB_SUSPEND
)
?
"host"
:
"remote"
);
ohci
->
hc_control
=
OHCI_USB_RESUME
;
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
20
);
/* no schedule here ! */
/* Some controllers (lucent) need a longer delay here */
mdelay
(
15
);
temp
=
readl
(
&
ohci
->
regs
->
control
);
temp
=
ohci
->
hc_control
&
OHCI_CTRL_HCFS
;
if
(
temp
!=
OHCI_USB_RESUME
)
{
err
(
"controller usb-%s won't resume"
,
dev
->
slot_name
);
ohci
->
disabled
=
1
;
return
-
EIO
;
}
/* Some chips likes being resumed first */
writel
(
OHCI_USB_OPER
,
&
ohci
->
regs
->
control
);
(
void
)
readl
(
&
ohci
->
regs
->
control
);
mdelay
(
3
);
/* Then re-enable operations */
spin_lock_irqsave
(
&
usb_ed_lock
,
flags
);
ohci
->
disabled
=
0
;
ohci
->
sleeping
=
0
;
ohci
->
hc_control
=
OHCI_CONTROL_INIT
|
OHCI_USB_OPER
;
if
(
!
ohci
->
ed_rm_list
[
0
]
&&
!
ohci
->
ed_rm_list
[
1
])
{
if
(
ohci
->
ed_controltail
)
ohci
->
hc_control
|=
OHCI_CTRL_CLE
;
if
(
ohci
->
ed_bulktail
)
ohci
->
hc_control
|=
OHCI_CTRL_BLE
;
}
writel
(
ohci
->
hc_control
,
&
ohci
->
regs
->
control
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrstatus
);
writel
(
OHCI_INTR_SF
,
&
ohci
->
regs
->
intrenable
);
/* Check for a pending done list */
writel
(
OHCI_INTR_WDH
,
&
ohci
->
regs
->
intrdisable
);
(
void
)
readl
(
&
ohci
->
regs
->
intrdisable
);
spin_unlock_irqrestore
(
&
usb_ed_lock
,
flags
);
#ifdef CONFIG_PMAC_PBOOK
if
(
_machine
==
_MACH_Pmac
)
enable_irq
(
ohci
->
irq
);
#endif
if
(
ohci
->
hcca
->
done_head
)
dl_done_list
(
ohci
,
dl_reverse_done_list
(
ohci
));
writel
(
OHCI_INTR_WDH
,
&
ohci
->
regs
->
intrenable
);
writel
(
OHCI_BLF
,
&
ohci
->
regs
->
cmdstatus
);
/* start bulk list */
writel
(
OHCI_CLF
,
&
ohci
->
regs
->
cmdstatus
);
/* start Control list */
break
;
default:
warn
(
"odd PCI resume for usb-%s"
,
dev
->
slot_name
);
}
/* controller is operational, extra resumes are harmless */
atomic_dec
(
&
ohci
->
resume_count
);
return
0
;
}
#endif
/* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static
const
struct
pci_device_id
__devinitdata
ohci_pci_ids
[]
=
{
{
/*
* AMD-756 [Viper] USB has a serious erratum when used with
* lowspeed devices like mice.
*/
vendor:
0x1022
,
device:
0x740c
,
subvendor:
PCI_ANY_ID
,
subdevice:
PCI_ANY_ID
,
driver_data:
OHCI_QUIRK_AMD756
,
}
,
{
/* handle any USB OHCI controller */
class:
((
PCI_CLASS_SERIAL_USB
<<
8
)
|
0x10
),
class_mask:
~
0
,
/* no matter who makes it */
vendor:
PCI_ANY_ID
,
device:
PCI_ANY_ID
,
subvendor:
PCI_ANY_ID
,
subdevice:
PCI_ANY_ID
,
},
{
/* end: all zeroes */
}
};
MODULE_DEVICE_TABLE
(
pci
,
ohci_pci_ids
);
static
struct
pci_driver
ohci_pci_driver
=
{
name:
"usb-ohci"
,
id_table:
&
ohci_pci_ids
[
0
],
probe:
ohci_pci_probe
,
remove:
__devexit_p
(
ohci_pci_remove
),
#ifdef CONFIG_PM
suspend:
ohci_pci_suspend
,
resume:
ohci_pci_resume
,
#endif
/* PM */
};
/*-------------------------------------------------------------------------*/
static
int
__init
ohci_hcd_init
(
void
)
{
return
pci_module_init
(
&
ohci_pci_driver
);
}
/*-------------------------------------------------------------------------*/
static
void
__exit
ohci_hcd_cleanup
(
void
)
{
pci_unregister_driver
(
&
ohci_pci_driver
);
}
module_init
(
ohci_hcd_init
);
module_exit
(
ohci_hcd_cleanup
);
MODULE_AUTHOR
(
DRIVER_AUTHOR
);
MODULE_DESCRIPTION
(
DRIVER_DESC
);
MODULE_LICENSE
(
"GPL"
);
EXPORT_SYMBOL
(
hc_add_ohci
);
EXPORT_SYMBOL
(
hc_remove_ohci
);
EXPORT_SYMBOL
(
hc_start
);
EXPORT_SYMBOL
(
hc_reset
);
EXPORT_SYMBOL
(
dl_done_list
);
EXPORT_SYMBOL
(
dl_reverse_done_list
);
EXPORT_SYMBOL
(
usb_ed_lock
);
drivers/usb/host/usb-ohci.h
View file @
4b59d1b2
...
...
@@ -403,6 +403,7 @@ typedef struct ohci {
/* PCI device handle, settings, ... */
struct
pci_dev
*
ohci_dev
;
const
char
*
slot_name
;
u8
pci_latency
;
struct
pci_pool
*
td_cache
;
struct
pci_pool
*
dev_cache
;
...
...
@@ -423,6 +424,10 @@ struct ohci_device {
// #define ohci_to_usb(ohci) ((ohci)->usb)
#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
/* hcd */
/* endpoint */
static
int
ep_link
(
ohci_t
*
ohci
,
ed_t
*
ed
);
...
...
@@ -447,11 +452,6 @@ static int rh_init_int_timer(struct urb * urb);
# define OHCI_MEM_FLAGS 0
#endif
#ifndef CONFIG_PCI
# error "usb-ohci currently requires PCI-based controllers"
/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
#endif
/* Recover a TD/ED using its collision chain */
static
void
*
...
...
@@ -641,3 +641,8 @@ dev_free (struct ohci *hc, struct ohci_device *dev)
pci_pool_free
(
hc
->
dev_cache
,
dev
,
dev
->
dma
);
}
extern
spinlock_t
usb_ed_lock
;
extern
void
dl_done_list
(
ohci_t
*
ohci
,
td_t
*
td_list
);
extern
td_t
*
dl_reverse_done_list
(
ohci_t
*
ohci
);
drivers/usb/storage/freecom.c
View file @
4b59d1b2
...
...
@@ -167,108 +167,6 @@ static void us_transfer_freecom(Scsi_Cmnd *srb, struct us_data* us, int transfer
srb
->
result
=
result
;
}
#if 0
/* Write a value to an ide register. */
static int
freecom_ide_write (struct us_data *us, int reg, int value)
{
freecom_udata_t extra = (freecom_udata_t) us->extra;
struct freecom_ide_out *ideout =
(struct freecom_ide_out *) extra->buffer;
int opipe;
int result, partial;
US_DEBUGP("IDE out 0x%02x <- 0x%02x\n", reg, value);
/* Get handles for both transports. */
opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
if (reg < 0 || reg > 8)
return USB_STOR_TRANSPORT_ERROR;
if (reg < 8)
reg |= 0x20;
else
reg = 0x0e;
ideout->Type = FCM_PACKET_IDE_WRITE | reg;
ideout->Pad = 0;
ideout->Value = cpu_to_le16 (value);
memset (ideout->Pad2, 0, sizeof (ideout->Pad2));
result = usb_stor_bulk_msg (us, ideout, opipe,
FCM_PACKET_LENGTH, &partial);
if (result != 0) {
if (result == -ENOENT)
return US_BULK_TRANSFER_ABORTED;
else
return USB_STOR_TRANSPORT_ERROR;
}
return USB_STOR_TRANSPORT_GOOD;
}
/* Read a value from an ide register. */
static int
freecom_ide_read (struct us_data *us, int reg, int *value)
{
freecom_udata_t extra = (freecom_udata_t) us->extra;
struct freecom_ide_in *idein =
(struct freecom_ide_in *) extra->buffer;
__u8 *buffer = extra->buffer;
int ipipe, opipe;
int result, partial;
int desired_length;
/* Get handles for both transports. */
opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in);
if (reg < 0 || reg > 8)
return USB_STOR_TRANSPORT_ERROR;
if (reg < 8)
reg |= 0x10;
else
reg = 0x0e;
US_DEBUGP("IDE in request for register 0x%02x\n", reg);
idein->Type = FCM_PACKET_IDE_READ | reg;
memset (idein->Pad, 0, sizeof (idein->Pad));
result = usb_stor_bulk_msg (us, idein, opipe,
FCM_PACKET_LENGTH, &partial);
if (result != 0) {
if (result == -ENOENT)
return US_BULK_TRANSFER_ABORTED;
else
return USB_STOR_TRANSPORT_ERROR;
}
desired_length = 1;
if (reg == 0x10)
desired_length = 2;
result = usb_stor_bulk_msg (us, buffer, ipipe,
desired_length, &partial);
if (result != 0) {
if (result == -ENOENT)
return US_BULK_TRANSFER_ABORTED;
else
return USB_STOR_TRANSPORT_ERROR;
}
US_DEBUGP("IDE in partial is %d\n", partial);
if (desired_length == 1)
*value = buffer[0];
else
*value = le16_to_cpu (*(__u16 *) buffer);
US_DEBUGP("IDE in 0x%02x -> 0x%02x\n", reg, *value);
return USB_STOR_TRANSPORT_GOOD;
}
#endif
static
int
freecom_readdata
(
Scsi_Cmnd
*
srb
,
struct
us_data
*
us
,
int
ipipe
,
int
opipe
,
int
count
)
...
...
@@ -292,8 +190,8 @@ freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
US_DEBUGP
(
"Freecom readdata xpot failure: r=%d, p=%d
\n
"
,
result
,
partial
);
/*
-ENOENT -- we canceled this transfer
*/
if
(
result
==
-
ENOENT
)
{
/*
has the current command been aborted?
*/
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_readdata(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -333,8 +231,8 @@ freecom_writedata (Scsi_Cmnd *srb, struct us_data *us,
US_DEBUGP
(
"Freecom writedata xpot failure: r=%d, p=%d
\n
"
,
result
,
partial
);
/*
-ENOENT -- we canceled this transfer
*/
if
(
result
==
-
ENOENT
)
{
/*
has the current command been aborted?
*/
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_writedata(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -396,8 +294,8 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP
(
"freecom xport failure: r=%d, p=%d
\n
"
,
result
,
partial
);
/*
-ENOENT --
we canceled this transfer */
if
(
result
==
-
ENOENT
)
{
/* we canceled this transfer */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -410,8 +308,9 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
result
=
usb_stor_bulk_msg
(
us
,
fst
,
ipipe
,
FCM_PACKET_LENGTH
,
&
partial
);
US_DEBUGP
(
"foo Status result %d %d
\n
"
,
result
,
partial
);
/* -ENOENT -- we canceled this transfer */
if
(
result
==
-
ENOENT
)
{
/* we canceled this transfer */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -448,8 +347,8 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP
(
"freecom xport failure: r=%d, p=%d
\n
"
,
result
,
partial
);
/*
-ENOENT --
we canceled this transfer */
if
(
result
==
-
ENOENT
)
{
/* we canceled this transfer */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -463,8 +362,8 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP
(
"bar Status result %d %d
\n
"
,
result
,
partial
);
/*
-ENOENT --
we canceled this transfer */
if
(
result
==
-
ENOENT
)
{
/* we canceled this transfer */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -524,7 +423,8 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
result
=
usb_stor_bulk_msg
(
us
,
fst
,
ipipe
,
FCM_PACKET_LENGTH
,
&
partial
);
US_DEBUG
(
pdump
((
void
*
)
fst
,
partial
));
if
(
result
==
-
ENOENT
)
{
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport: transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
@@ -552,7 +452,8 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP
(
"FCM: Waiting for status
\n
"
);
result
=
usb_stor_bulk_msg
(
us
,
fst
,
ipipe
,
FCM_PACKET_LENGTH
,
&
partial
);
if
(
result
==
-
ENOENT
)
{
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"freecom_transport: transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
...
...
drivers/usb/storage/scsiglue.c
View file @
4b59d1b2
...
...
@@ -386,9 +386,9 @@ Scsi_Host_Template usb_stor_host_template = {
unsigned
char
usb_stor_sense_notready
[
18
]
=
{
[
0
]
=
0x70
,
/* current error */
[
2
]
=
0x02
,
/* not ready */
[
5
]
=
0x0a
,
/* additional length */
[
1
0
]
=
0x04
,
/* not ready */
[
1
1
]
=
0x03
/* manual intervention */
[
7
]
=
0x0a
,
/* additional length */
[
1
2
]
=
0x04
,
/* not ready */
[
1
3
]
=
0x03
/* manual intervention */
};
#define USB_STOR_SCSI_SENSE_HDRSZ 4
...
...
drivers/usb/storage/transport.c
View file @
4b59d1b2
...
...
@@ -986,9 +986,11 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
clear_bit
(
IP_WANTED
,
&
us
->
bitflags
);
}
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_control_msg(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* STALL must be cleared when it is detected */
if
(
result
==
-
EPIPE
)
{
...
...
@@ -996,9 +998,12 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
result
=
usb_stor_clear_halt
(
us
,
usb_sndctrlpipe
(
us
->
pusb_dev
,
0
));
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_control_msg(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
return
USB_STOR_TRANSPORT_FAILED
;
}
...
...
@@ -1098,9 +1103,11 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* check the return code for the command */
US_DEBUGP
(
"Call to usb_stor_control_msg() returned %d
\n
"
,
result
);
if
(
result
<
0
)
{
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_CB_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* a stall is a fatal condition from the device */
if
(
result
==
-
EPIPE
)
{
...
...
@@ -1108,9 +1115,11 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
result
=
usb_stor_clear_halt
(
us
,
usb_sndctrlpipe
(
us
->
pusb_dev
,
0
));
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_CB_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
return
USB_STOR_TRANSPORT_FAILED
;
}
...
...
@@ -1215,18 +1224,22 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
&
partial
);
US_DEBUGP
(
"Bulk command transfer result=%d
\n
"
,
result
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* if we stall, we need to clear it before we go on */
if
(
result
==
-
EPIPE
)
{
US_DEBUGP
(
"clearing endpoint halt for pipe 0x%x
\n
"
,
pipe
);
result
=
usb_stor_clear_halt
(
us
,
pipe
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
result
=
-
EPIPE
;
}
else
if
(
result
)
{
/* unknown error -- we've got a problem */
...
...
@@ -1259,36 +1272,44 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
result
=
usb_stor_bulk_msg
(
us
,
&
bcs
,
pipe
,
US_BULK_CS_WRAP_LEN
,
&
partial
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* did the attempt to read the CSW fail? */
if
(
result
==
-
EPIPE
)
{
US_DEBUGP
(
"clearing endpoint halt for pipe 0x%x
\n
"
,
pipe
);
result
=
usb_stor_clear_halt
(
us
,
pipe
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* get the status again */
US_DEBUGP
(
"Attempting to get CSW (2nd try)...
\n
"
);
result
=
usb_stor_bulk_msg
(
us
,
&
bcs
,
pipe
,
US_BULK_CS_WRAP_LEN
,
&
partial
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
/* if it fails again, we need a reset and return an error*/
if
(
result
==
-
EPIPE
)
{
US_DEBUGP
(
"clearing halt for pipe 0x%x
\n
"
,
pipe
);
result
=
usb_stor_clear_halt
(
us
,
pipe
);
/* if the command was aborted, indicate that */
if
(
result
==
-
ENOENT
)
return
USB_STOR_TRANSPORT_ABORTED
;
/* did we abort this command? */
if
(
atomic_read
(
&
us
->
sm_state
)
==
US_STATE_ABORTING
)
{
US_DEBUGP
(
"usb_stor_Bulk_transport(): transfer aborted
\n
"
);
return
US_BULK_TRANSFER_ABORTED
;
}
return
USB_STOR_TRANSPORT_ERROR
;
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment