forked from luck/tmp_suning_uos_patched
USB: extend ehci-fsl and fsl_udc_core driver for OTG operation
Signed-off-by: Anatolij Gustschin <agust@denx.de> Cc: Li Yang <leoli@freescale.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
0807c500a1
commit
83722bc943
@ -353,6 +353,19 @@ static void dr_controller_stop(struct fsl_udc *udc)
|
|||||||
{
|
{
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
|
/* if we're in OTG mode, and the Host is currently using the port,
|
||||||
|
* stop now and don't rip the controller out from under the
|
||||||
|
* ehci driver
|
||||||
|
*/
|
||||||
|
if (udc->gadget.is_otg) {
|
||||||
|
if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
|
||||||
|
pr_debug("udc: Leaving early\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* disable all INTR */
|
/* disable all INTR */
|
||||||
fsl_writel(0, &dr_regs->usbintr);
|
fsl_writel(0, &dr_regs->usbintr);
|
||||||
|
|
||||||
@ -1668,6 +1681,9 @@ static void port_change_irq(struct fsl_udc *udc)
|
|||||||
{
|
{
|
||||||
u32 speed;
|
u32 speed;
|
||||||
|
|
||||||
|
if (udc->bus_reset)
|
||||||
|
udc->bus_reset = 0;
|
||||||
|
|
||||||
/* Bus resetting is finished */
|
/* Bus resetting is finished */
|
||||||
if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
|
if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
|
||||||
/* Get the speed */
|
/* Get the speed */
|
||||||
@ -1775,6 +1791,8 @@ static void reset_irq(struct fsl_udc *udc)
|
|||||||
|
|
||||||
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
|
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
|
||||||
VDBG("Bus reset");
|
VDBG("Bus reset");
|
||||||
|
/* Bus is reseting */
|
||||||
|
udc->bus_reset = 1;
|
||||||
/* Reset all the queues, include XD, dTD, EP queue
|
/* Reset all the queues, include XD, dTD, EP queue
|
||||||
* head and TR Queue */
|
* head and TR Queue */
|
||||||
reset_queues(udc);
|
reset_queues(udc);
|
||||||
@ -1852,6 +1870,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
|||||||
|
|
||||||
/* Reset Received */
|
/* Reset Received */
|
||||||
if (irq_src & USB_STS_RESET) {
|
if (irq_src & USB_STS_RESET) {
|
||||||
|
VDBG("reset int");
|
||||||
reset_irq(udc);
|
reset_irq(udc);
|
||||||
status = IRQ_HANDLED;
|
status = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@ -1909,11 +1928,30 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable DR IRQ reg and Set usbcmd reg Run bit */
|
if (udc_controller->transceiver) {
|
||||||
dr_controller_run(udc_controller);
|
/* Suspend the controller until OTG enable it */
|
||||||
udc_controller->usb_state = USB_STATE_ATTACHED;
|
udc_controller->stopped = 1;
|
||||||
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
printk(KERN_INFO "Suspend udc for OTG auto detect\n");
|
||||||
udc_controller->ep0_dir = 0;
|
|
||||||
|
/* connect to bus through transceiver */
|
||||||
|
if (udc_controller->transceiver) {
|
||||||
|
retval = otg_set_peripheral(udc_controller->transceiver,
|
||||||
|
&udc_controller->gadget);
|
||||||
|
if (retval < 0) {
|
||||||
|
ERR("can't bind to transceiver\n");
|
||||||
|
driver->unbind(&udc_controller->gadget);
|
||||||
|
udc_controller->gadget.dev.driver = 0;
|
||||||
|
udc_controller->driver = 0;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Enable DR IRQ reg and set USBCMD reg Run bit */
|
||||||
|
dr_controller_run(udc_controller);
|
||||||
|
udc_controller->usb_state = USB_STATE_ATTACHED;
|
||||||
|
udc_controller->ep0_state = WAIT_FOR_SETUP;
|
||||||
|
udc_controller->ep0_dir = 0;
|
||||||
|
}
|
||||||
printk(KERN_INFO "%s: bind to driver %s\n",
|
printk(KERN_INFO "%s: bind to driver %s\n",
|
||||||
udc_controller->gadget.name, driver->driver.name);
|
udc_controller->gadget.name, driver->driver.name);
|
||||||
|
|
||||||
@ -2374,17 +2412,30 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
spin_lock_init(&udc_controller->lock);
|
spin_lock_init(&udc_controller->lock);
|
||||||
udc_controller->stopped = 1;
|
udc_controller->stopped = 1;
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_OTG
|
||||||
|
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||||
|
udc_controller->transceiver = otg_get_transceiver();
|
||||||
|
if (!udc_controller->transceiver) {
|
||||||
|
ERR("Can't find OTG driver!\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto err_kfree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request_mem_region(res->start, res->end - res->start + 1,
|
if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
|
||||||
driver_name)) {
|
if (!request_mem_region(res->start, res->end - res->start + 1,
|
||||||
ERR("request mem region for %s failed\n", pdev->name);
|
driver_name)) {
|
||||||
ret = -EBUSY;
|
ERR("request mem region for %s failed\n", pdev->name);
|
||||||
goto err_kfree;
|
ret = -EBUSY;
|
||||||
|
goto err_kfree;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dr_regs = ioremap(res->start, resource_size(res));
|
dr_regs = ioremap(res->start, resource_size(res));
|
||||||
@ -2455,9 +2506,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize usb hw reg except for regs for EP,
|
if (!udc_controller->transceiver) {
|
||||||
* leave usbintr reg untouched */
|
/* initialize usb hw reg except for regs for EP,
|
||||||
dr_controller_setup(udc_controller);
|
* leave usbintr reg untouched */
|
||||||
|
dr_controller_setup(udc_controller);
|
||||||
|
}
|
||||||
|
|
||||||
fsl_udc_clk_finalize(pdev);
|
fsl_udc_clk_finalize(pdev);
|
||||||
|
|
||||||
@ -2477,6 +2530,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
|
|
||||||
|
if (udc_controller->transceiver)
|
||||||
|
udc_controller->gadget.is_otg = 1;
|
||||||
|
|
||||||
/* setup QH and epctrl for ep0 */
|
/* setup QH and epctrl for ep0 */
|
||||||
ep0_setup(udc_controller);
|
ep0_setup(udc_controller);
|
||||||
|
|
||||||
@ -2521,7 +2577,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
err_iounmap_noclk:
|
err_iounmap_noclk:
|
||||||
iounmap(dr_regs);
|
iounmap(dr_regs);
|
||||||
err_release_mem_region:
|
err_release_mem_region:
|
||||||
release_mem_region(res->start, res->end - res->start + 1);
|
if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
|
||||||
|
release_mem_region(res->start, res->end - res->start + 1);
|
||||||
err_kfree:
|
err_kfree:
|
||||||
kfree(udc_controller);
|
kfree(udc_controller);
|
||||||
udc_controller = NULL;
|
udc_controller = NULL;
|
||||||
@ -2555,7 +2612,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
|
|||||||
dma_pool_destroy(udc_controller->td_pool);
|
dma_pool_destroy(udc_controller->td_pool);
|
||||||
free_irq(udc_controller->irq, udc_controller);
|
free_irq(udc_controller->irq, udc_controller);
|
||||||
iounmap(dr_regs);
|
iounmap(dr_regs);
|
||||||
release_mem_region(res->start, res->end - res->start + 1);
|
if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
|
||||||
|
release_mem_region(res->start, res->end - res->start + 1);
|
||||||
|
|
||||||
device_unregister(&udc_controller->gadget.dev);
|
device_unregister(&udc_controller->gadget.dev);
|
||||||
/* free udc --wait for the release() finished */
|
/* free udc --wait for the release() finished */
|
||||||
@ -2598,6 +2656,62 @@ static int fsl_udc_resume(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct fsl_udc *udc = udc_controller;
|
||||||
|
u32 mode, usbcmd;
|
||||||
|
|
||||||
|
mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
|
||||||
|
|
||||||
|
pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the controller is already stopped, then this must be a
|
||||||
|
* PM suspend. Remember this fact, so that we will leave the
|
||||||
|
* controller stopped at PM resume time.
|
||||||
|
*/
|
||||||
|
if (udc->stopped) {
|
||||||
|
pr_debug("gadget already stopped, leaving early\n");
|
||||||
|
udc->already_stopped = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != USB_MODE_CTRL_MODE_DEVICE) {
|
||||||
|
pr_debug("gadget not in device mode, leaving early\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stop the controller */
|
||||||
|
usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP;
|
||||||
|
fsl_writel(usbcmd, &dr_regs->usbcmd);
|
||||||
|
|
||||||
|
udc->stopped = 1;
|
||||||
|
|
||||||
|
pr_info("USB Gadget suspended\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_udc_otg_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
pr_debug("%s(): stopped %d already_stopped %d\n", __func__,
|
||||||
|
udc_controller->stopped, udc_controller->already_stopped);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the controller was stopped at suspend time, then
|
||||||
|
* don't resume it now.
|
||||||
|
*/
|
||||||
|
if (udc_controller->already_stopped) {
|
||||||
|
udc_controller->already_stopped = 0;
|
||||||
|
pr_debug("gadget was already stopped, leaving early\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("USB Gadget resume\n");
|
||||||
|
|
||||||
|
return fsl_udc_resume(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
Register entry point for the peripheral controller driver
|
Register entry point for the peripheral controller driver
|
||||||
--------------------------------------------------------------------------*/
|
--------------------------------------------------------------------------*/
|
||||||
@ -2610,6 +2724,9 @@ static struct platform_driver udc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = (char *)driver_name,
|
.name = (char *)driver_name,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
/* udc suspend/resume called from OTG driver */
|
||||||
|
.suspend = fsl_udc_otg_suspend,
|
||||||
|
.resume = fsl_udc_otg_resume,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -476,6 +476,7 @@ struct fsl_udc {
|
|||||||
unsigned vbus_active:1;
|
unsigned vbus_active:1;
|
||||||
unsigned stopped:1;
|
unsigned stopped:1;
|
||||||
unsigned remote_wakeup:1;
|
unsigned remote_wakeup:1;
|
||||||
|
unsigned already_stopped:1;
|
||||||
unsigned big_endian_desc:1;
|
unsigned big_endian_desc:1;
|
||||||
|
|
||||||
struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
|
struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
|
||||||
@ -487,6 +488,7 @@ struct fsl_udc {
|
|||||||
dma_addr_t ep_qh_dma; /* dma address of QH */
|
dma_addr_t ep_qh_dma; /* dma address of QH */
|
||||||
|
|
||||||
u32 max_pipes; /* Device max pipes */
|
u32 max_pipes; /* Device max pipes */
|
||||||
|
u32 bus_reset; /* Device is bus resetting */
|
||||||
u32 resume_state; /* USB state to resume */
|
u32 resume_state; /* USB state to resume */
|
||||||
u32 usb_state; /* USB current state */
|
u32 usb_state; /* USB current state */
|
||||||
u32 ep0_state; /* Endpoint zero state */
|
u32 ep0_state; /* Endpoint zero state */
|
||||||
|
@ -117,6 +117,9 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
|||||||
|
|
||||||
pdata->regs = hcd->regs;
|
pdata->regs = hcd->regs;
|
||||||
|
|
||||||
|
if (pdata->power_budget)
|
||||||
|
hcd->power_budget = pdata->power_budget;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do platform specific init: check the clock, grab/config pins, etc.
|
* do platform specific init: check the clock, grab/config pins, etc.
|
||||||
*/
|
*/
|
||||||
@ -134,6 +137,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
|||||||
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||||
if (retval != 0)
|
if (retval != 0)
|
||||||
goto err4;
|
goto err4;
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_OTG
|
||||||
|
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
|
ehci->transceiver = otg_get_transceiver();
|
||||||
|
dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n",
|
||||||
|
hcd, ehci, ehci->transceiver);
|
||||||
|
|
||||||
|
if (ehci->transceiver) {
|
||||||
|
retval = otg_set_host(ehci->transceiver,
|
||||||
|
&ehci_to_hcd(ehci)->self);
|
||||||
|
if (retval) {
|
||||||
|
if (ehci->transceiver)
|
||||||
|
put_device(ehci->transceiver->dev);
|
||||||
|
goto err4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dev_err(&pdev->dev, "can't find transceiver\n");
|
||||||
|
retval = -ENODEV;
|
||||||
|
goto err4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
err4:
|
err4:
|
||||||
@ -164,6 +191,12 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
|
|||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
|
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
|
if (ehci->transceiver) {
|
||||||
|
otg_set_host(ehci->transceiver, NULL);
|
||||||
|
put_device(ehci->transceiver->dev);
|
||||||
|
}
|
||||||
|
|
||||||
usb_remove_hcd(hcd);
|
usb_remove_hcd(hcd);
|
||||||
|
|
||||||
@ -544,6 +577,38 @@ static struct dev_pm_ops ehci_fsl_pm_ops = {
|
|||||||
#define EHCI_FSL_PM_OPS NULL
|
#define EHCI_FSL_PM_OPS NULL
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_OTG
|
||||||
|
static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
if (!port)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
port--;
|
||||||
|
|
||||||
|
/* start port reset before HNP protocol time out */
|
||||||
|
status = readl(&ehci->regs->port_status[port]);
|
||||||
|
if (!(status & PORT_CONNECT))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* khubd will finish the reset later */
|
||||||
|
if (ehci_is_TDI(ehci)) {
|
||||||
|
writel(PORT_RESET |
|
||||||
|
(status & ~(PORT_CSC | PORT_PEC | PORT_OCC)),
|
||||||
|
&ehci->regs->port_status[port]);
|
||||||
|
} else {
|
||||||
|
writel(PORT_RESET, &ehci->regs->port_status[port]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define ehci_start_port_reset NULL
|
||||||
|
#endif /* CONFIG_USB_OTG */
|
||||||
|
|
||||||
|
|
||||||
static const struct hc_driver ehci_fsl_hc_driver = {
|
static const struct hc_driver ehci_fsl_hc_driver = {
|
||||||
.description = hcd_name,
|
.description = hcd_name,
|
||||||
.product_desc = "Freescale On-Chip EHCI Host Controller",
|
.product_desc = "Freescale On-Chip EHCI Host Controller",
|
||||||
@ -583,6 +648,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
|||||||
.hub_control = ehci_hub_control,
|
.hub_control = ehci_hub_control,
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
|
.start_port_reset = ehci_start_port_reset,
|
||||||
.relinquish_port = ehci_relinquish_port,
|
.relinquish_port = ehci_relinquish_port,
|
||||||
.port_handed_over = ehci_port_handed_over,
|
.port_handed_over = ehci_port_handed_over,
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
#include <linux/usb/otg.h>
|
||||||
|
|
||||||
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
|
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
|
||||||
|
|
||||||
@ -801,6 +802,13 @@ static int ehci_hub_control (
|
|||||||
goto error;
|
goto error;
|
||||||
if (ehci->no_selective_suspend)
|
if (ehci->no_selective_suspend)
|
||||||
break;
|
break;
|
||||||
|
#ifdef CONFIG_USB_OTG
|
||||||
|
if ((hcd->self.otg_port == (wIndex + 1))
|
||||||
|
&& hcd->self.b_hnp_enable) {
|
||||||
|
otg_start_hnp(ehci->transceiver);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!(temp & PORT_SUSPEND))
|
if (!(temp & PORT_SUSPEND))
|
||||||
break;
|
break;
|
||||||
if ((temp & PORT_PE) == 0)
|
if ((temp & PORT_PE) == 0)
|
||||||
|
@ -161,6 +161,10 @@ struct ehci_hcd { /* one per controller */
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
struct dentry *debug_dir;
|
struct dentry *debug_dir;
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
* OTG controllers and transceivers need software interaction
|
||||||
|
*/
|
||||||
|
struct otg_transceiver *transceiver;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
||||||
|
@ -72,6 +72,7 @@ struct fsl_usb2_platform_data {
|
|||||||
void (*exit)(struct platform_device *);
|
void (*exit)(struct platform_device *);
|
||||||
void __iomem *regs; /* ioremap'd register base */
|
void __iomem *regs; /* ioremap'd register base */
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
unsigned power_budget; /* hcd->power_budget */
|
||||||
unsigned big_endian_mmio:1;
|
unsigned big_endian_mmio:1;
|
||||||
unsigned big_endian_desc:1;
|
unsigned big_endian_desc:1;
|
||||||
unsigned es:1; /* need USBMODE:ES */
|
unsigned es:1; /* need USBMODE:ES */
|
||||||
|
Loading…
Reference in New Issue
Block a user