first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <drivers/apple/gpio.h>
|
||||
#include <drivers/spi.h>
|
||||
#include <platform.h>
|
||||
#include <platform/gpio.h>
|
||||
#include <platform/gpiodef.h>
|
||||
#include <platform/memconfig.h>
|
||||
#include <platform/soc/hwregbase.h>
|
||||
#if GPIO_VERSION > 5
|
||||
#include <platform/soc/pmgr.h>
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
uintptr_t base_addr;
|
||||
uint32_t pin_count;
|
||||
} gpio_cfgs[GPIOC_COUNT] = {
|
||||
#if GPIOC_COUNT > 0
|
||||
{ GPIO_BASE_ADDR, GPIO_GROUP_COUNT * GPIOPADPINS },
|
||||
#endif
|
||||
#if GPIOC_COUNT > 1
|
||||
{ GPIO_1_BASE_ADDR, GPIO_1_GROUP_COUNT * GPIOPADPINS },
|
||||
#endif
|
||||
};
|
||||
|
||||
uint32_t gpio_read(gpio_t gpio)
|
||||
{
|
||||
uint8_t gpioc = GPIO2CONTROLLER(gpio);
|
||||
uint8_t pad = GPIO2PAD(gpio);
|
||||
uint8_t pin = GPIO2PIN(gpio);
|
||||
uint8_t gpion = pad * GPIOPADPINS + pin;
|
||||
|
||||
#if WITH_HW_SPI && defined(GPIO_PAD_SPI)
|
||||
if (pad == GPIO_PAD_SPI) return spi_gpio_read(pin);
|
||||
#endif
|
||||
|
||||
return (rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) & 1) ? 1UL : 0UL;
|
||||
}
|
||||
|
||||
void gpio_write(gpio_t gpio, uint32_t val)
|
||||
{
|
||||
uint32_t cfg_val, cfg_mask;
|
||||
uint8_t gpioc = GPIO2CONTROLLER(gpio);
|
||||
uint8_t pad = GPIO2PAD(gpio);
|
||||
uint8_t pin = GPIO2PIN(gpio);
|
||||
uint8_t gpion = pad * GPIOPADPINS + pin;
|
||||
|
||||
#if WITH_HW_SPI && defined(GPIO_PAD_SPI)
|
||||
if (pad == GPIO_PAD_SPI) {
|
||||
spi_gpio_write(pin, val);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
cfg_val = (val) ? CFG_OUT_1 : CFG_OUT_0;
|
||||
cfg_mask = INPUT_ENABLE | FUNC_MASK | INT_MASKED | CFG_MASK | DATA_1;
|
||||
|
||||
rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) = (rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) & ~cfg_mask) | (cfg_val & cfg_mask);
|
||||
}
|
||||
|
||||
void gpio_safe_reconfigure(uint8_t gpioc, uint8_t gpion, uint32_t config)
|
||||
{
|
||||
uint32_t old_config;
|
||||
uint32_t new_input_enable;
|
||||
uint32_t old_input_enable;
|
||||
|
||||
old_config = rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion);
|
||||
new_input_enable = config & INPUT_ENABLE;
|
||||
|
||||
if (new_input_enable == 0) {
|
||||
rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) = old_config & ~INPUT_ENABLE;
|
||||
old_input_enable = 0;
|
||||
} else {
|
||||
old_input_enable = old_config & INPUT_ENABLE;
|
||||
}
|
||||
|
||||
rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) = (config & ~INPUT_ENABLE) | old_input_enable;
|
||||
|
||||
if (new_input_enable != 0 && old_input_enable == 0)
|
||||
rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) = config;
|
||||
}
|
||||
|
||||
void gpio_configure(gpio_t gpio, uint32_t config)
|
||||
{
|
||||
uint32_t cfg_val, cfg_mask;
|
||||
uint8_t gpioc = GPIO2CONTROLLER(gpio);
|
||||
uint8_t pad = GPIO2PAD(gpio);
|
||||
uint8_t pin = GPIO2PIN(gpio);
|
||||
uint8_t gpion = pad * GPIOPADPINS + pin;
|
||||
|
||||
#if WITH_HW_SPI && defined(GPIO_PAD_SPI)
|
||||
if (pad == GPIO_PAD_SPI) {
|
||||
spi_gpio_configure(pin, config);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
cfg_mask = INPUT_ENABLE | FUNC_MASK | INT_MASKED | CFG_MASK;
|
||||
|
||||
switch (config) {
|
||||
case GPIO_CFG_IN : cfg_val = CFG_IN; break;
|
||||
case GPIO_CFG_OUT_0 : cfg_val = CFG_OUT_0; cfg_mask |= DATA_1; break;
|
||||
case GPIO_CFG_OUT_1 : cfg_val = CFG_OUT_1; cfg_mask |= DATA_1; break;
|
||||
case GPIO_CFG_OUT : cfg_val = CFG_OUT; break;
|
||||
case GPIO_CFG_FUNC0 : cfg_val = CFG_FUNC0; break;
|
||||
case GPIO_CFG_FUNC1 : cfg_val = CFG_FUNC1; break;
|
||||
case GPIO_CFG_FUNC2 : cfg_val = CFG_FUNC2; break;
|
||||
case GPIO_CFG_DFLT :
|
||||
cfg_val = platform_get_default_gpio_cfg(gpioc)[gpion]; cfg_mask |= PULL_MASK | DATA_1; break;
|
||||
default : return;
|
||||
}
|
||||
|
||||
gpio_safe_reconfigure(gpioc, gpion, (rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) & ~cfg_mask) | (cfg_val & cfg_mask));
|
||||
}
|
||||
|
||||
void gpio_configure_pupdn(gpio_t gpio, int32_t pupdn)
|
||||
{
|
||||
uint8_t gpioc = GPIO2CONTROLLER(gpio);
|
||||
uint8_t pad = GPIO2PAD(gpio);
|
||||
uint8_t pin = GPIO2PIN(gpio);
|
||||
uint8_t gpion = pad * GPIOPADPINS + pin;
|
||||
uint32_t cfg_val;
|
||||
|
||||
if (pupdn < GPIO_NO_PUPDN) {
|
||||
cfg_val = PULL_DOWN;
|
||||
} else if (pupdn > GPIO_NO_PUPDN) {
|
||||
#if GPIO_VERSION < 5
|
||||
cfg_val = PULL_UP;
|
||||
#else
|
||||
cfg_val = (pupdn > GPIO_PUP) ? PULL_UP_STRONG : PULL_UP;
|
||||
#endif
|
||||
} else {
|
||||
/* GPIO_NO_PUPDN */
|
||||
cfg_val = PULL_NONE;
|
||||
}
|
||||
|
||||
rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) = (rGPIOCFG(gpio_cfgs[gpioc].base_addr, gpion) & ~PULL_MASK) | (cfg_val & PULL_MASK);
|
||||
}
|
||||
|
||||
|
||||
int gpio_init_pinconfig(void)
|
||||
{
|
||||
uint8_t cnt, gpioc;
|
||||
|
||||
for (gpioc = 0; gpioc < GPIOC_COUNT; gpioc++) {
|
||||
const uint32_t *pin_config = platform_get_default_gpio_cfg(gpioc);
|
||||
|
||||
for (cnt = 0; cnt < gpio_cfgs[gpioc].pin_count; cnt++) {
|
||||
gpio_safe_reconfigure(gpioc, cnt, pin_config[cnt]);
|
||||
}
|
||||
|
||||
#ifdef FMI_SLEW_RATE
|
||||
// Adjust global FMI pads slew rate.
|
||||
rGPIO_SR_EN(gpio_cfgs[gpioc].base_addr) = (rGPIO_SR_EN(gpio_cfgs[gpioc].base_addr) & ~1) | (FMI_SLEW_RATE & 1);
|
||||
#endif
|
||||
|
||||
#ifdef FMI_DIFF_SEL
|
||||
// Configure FMI differential pin selection
|
||||
rGPIO_FMI_DIFF_SEL(gpio_cfgs[gpioc].base_addr) = FMI_DIFF_SEL;
|
||||
#endif
|
||||
|
||||
#if GPIO_VERSION > 0
|
||||
// Enable interrupt when pin not configured as GPIO
|
||||
rGPIO_NPL_IN_EN(gpio_cfgs[gpioc].base_addr) = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GPIO_VERSION > 6
|
||||
#if !APPLICATION_SECUREROM
|
||||
// The GPIO Micro-Architecture Specification v1.3, section 4.1 (5) says:
|
||||
//
|
||||
// "To control the XTAL_DRIVE strength software needs to program
|
||||
// the GPIO_MISC_CTRL[XTAL_DRIVE_STRENGTH] bit. This should be
|
||||
// programmed only after 140,000 24MHZ clocks to allow the xtal
|
||||
// to lock to 24MHz. Once this change is made we should allow
|
||||
// 20,000 clocks for a stable duty cycle.
|
||||
//
|
||||
// Since we're not doing this in the SecureROM we're definitely far
|
||||
// enough along here that we've met the 140K cycles requirement.
|
||||
rGPIO_MISC_CTRL(gpio_cfgs[0].base_addr) = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_fixup_pinconfig(const uint32_t *fixup_list) {
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; fixup_list[i * 2] != UINT32_MAX; i ++) {
|
||||
gpio_t gpio = fixup_list[(i * 2) + 0];
|
||||
uint8_t gpioc = GPIO2CONTROLLER(gpio);
|
||||
uint8_t pad = GPIO2PAD(gpio);
|
||||
uint8_t pin = GPIO2PIN(gpio);
|
||||
uint8_t gpion = pad * GPIOPADPINS + pin;
|
||||
|
||||
gpio_safe_reconfigure(gpioc, gpion, fixup_list[(i * 2) + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
int gpio_init_memory(uint32_t drive_strength)
|
||||
{
|
||||
gpio_t gpio;
|
||||
uint32_t pin, pad, gpion, config;
|
||||
int32_t cnt, pin_count = sizeof(memory_interface_gpios) / sizeof(memory_interface_gpios[0]);
|
||||
|
||||
/*
|
||||
* XXXX only one gpio controller is supported
|
||||
*/
|
||||
|
||||
config = CFG_IN;
|
||||
#if GPIO_VERSION < 5
|
||||
switch (drive_strength) {
|
||||
case GPIO_DRIVE_X1 : config |= DRIVE_X1; break;
|
||||
case GPIO_DRIVE_X2 : config |= DRIVE_X2; break;
|
||||
case GPIO_DRIVE_X3 : config |= DRIVE_X3; break;
|
||||
case GPIO_DRIVE_X4 : config |= DRIVE_X4; break;
|
||||
case GPIO_DRIVE_X6 : config |= DRIVE_X6; break;
|
||||
}
|
||||
#else
|
||||
switch (drive_strength) {
|
||||
case GPIO_DRIVE_S0 : config |= DRIVE_S0; break;
|
||||
case GPIO_DRIVE_S1 : config |= DRIVE_S1; break;
|
||||
case GPIO_DRIVE_S2 : config |= DRIVE_S2; break;
|
||||
case GPIO_DRIVE_S3 : config |= DRIVE_S3; break;
|
||||
case GPIO_DRIVE_S4 : config |= DRIVE_S4; break;
|
||||
case GPIO_DRIVE_S5 : config |= DRIVE_S5; break;
|
||||
case GPIO_DRIVE_S6 : config |= DRIVE_S6; break;
|
||||
case GPIO_DRIVE_S7 : config |= DRIVE_S7; break;
|
||||
case GPIO_DRIVE_S8 : config |= DRIVE_S8; break;
|
||||
case GPIO_DRIVE_S9 : config |= DRIVE_S9; break;
|
||||
case GPIO_DRIVE_S10 : config |= DRIVE_S10; break;
|
||||
case GPIO_DRIVE_S11 : config |= DRIVE_S11; break;
|
||||
case GPIO_DRIVE_S12 : config |= DRIVE_S12; break;
|
||||
case GPIO_DRIVE_S13 : config |= DRIVE_S13; break;
|
||||
case GPIO_DRIVE_S14 : config |= DRIVE_S14; break;
|
||||
case GPIO_DRIVE_S15 : config |= DRIVE_S15; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (cnt = 0; cnt < pin_count; cnt++) {
|
||||
gpio = memory_interface_gpios[cnt];
|
||||
pad = GPIO2PAD(gpio);
|
||||
pin = GPIO2PIN(gpio);
|
||||
gpion = pad * GPIOPADPINS + pin;
|
||||
|
||||
gpio_safe_reconfigure(0, gpion, config);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t gpio_get_board_id(void)
|
||||
{
|
||||
#if GPIO_VERSION > 5
|
||||
// The board ID pins must have been sampled by the time this request is made.
|
||||
RELEASE_ASSERT(rGPIO_PINSTRAPS_VALID(gpio_cfgs[0].base_addr) & 1);
|
||||
|
||||
// Return the sampled board ID value.
|
||||
return MINIPMGR_SECURITY_GPIO_STRAPS_BOARD_ID_XTRCT(rMINIPMGR_SECURITY_GPIO_STRAPS);
|
||||
#else
|
||||
panic("gpio_get_board_id() not supported for this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if APPLICATION_IBOOT
|
||||
|
||||
int gpio_diag_pinconfig(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* APPLICATION_IBOOT */
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2013 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#ifndef __APPLE_GPIO_H
|
||||
#define __APPLE_GPIO_H
|
||||
|
||||
#include <platform/soc/hwregbase.h>
|
||||
|
||||
#if !defined(GPIOC_COUNT)
|
||||
# define GPIOC_COUNT (1)
|
||||
#endif // GPIOC_COUNT
|
||||
|
||||
/* Register Offsets for Apple GPIOs */
|
||||
#define rGPIOCFG(_b, _n) (*(volatile u_int32_t *)((_b) + 0x000 + (_n) * 4))
|
||||
#define rGPIOINT(_b, _n) (*(volatile u_int32_t *)((_b) + 0x800 + (_n) * 4))
|
||||
#if GPIO_VERSION < 3
|
||||
#define rGPIO_SR_EN(_b) (*(volatile u_int32_t *)((_b) + 0xC4C))
|
||||
#define rGPIO_FMI_DIFF_SEL(_b) (*(volatile u_int32_t *)((_b) + 0xC4C))
|
||||
#endif
|
||||
#define rGPIO_NPL_IN_EN(_b) (*(volatile u_int32_t *)((_b) + 0xC48))
|
||||
#if GPIO_VERSION > 5
|
||||
#define rGPIO_PINSTRAPS_VALID(_b) (*(volatile u_int32_t *)((_b) + 0xC4C))
|
||||
#define rGPIO_MISC_CTRL(_b) (*(volatile u_int32_t *)((_b) + 0xC50))
|
||||
#endif
|
||||
|
||||
/* Base Pin Defines for Apple GPIOs */
|
||||
|
||||
#define GPIOPADPINS (8)
|
||||
|
||||
#define GPIO2PIN(gpio) ((gpio) & (GPIOPADPINS - 1))
|
||||
#define GPIO2PAD(gpio) (((gpio) >> 8) & 0xFF)
|
||||
#define GPIO2CONTROLLER(gpio) (((gpio) >> 24) & 0xFF)
|
||||
|
||||
#define DATA_0 (0 << 0)
|
||||
#define DATA_1 (1 << 0)
|
||||
|
||||
#define CFG_GP_IN (0 << 1)
|
||||
#define CFG_GP_OUT (1 << 1)
|
||||
#define CFG_INT_LVL_HI (2 << 1)
|
||||
#define CFG_INT_LVL_LO (3 << 1)
|
||||
#define CFG_INT_EDG_RIS (4 << 1)
|
||||
#define CFG_INT_EDG_FAL (5 << 1)
|
||||
#define CFG_INT_EDG_ANY (6 << 1)
|
||||
#define CFG_DISABLE (7 << 1)
|
||||
#define CFG_MASK (7 << 1)
|
||||
|
||||
#if GPIO_VERSION < 3
|
||||
#define INT_MASKED (1 << 4)
|
||||
#endif
|
||||
|
||||
#define FUNC_GPIO (0 << 5)
|
||||
#define FUNC_ALT0 (1 << 5)
|
||||
#define FUNC_ALT1 (2 << 5)
|
||||
#define FUNC_ALT2 (3 << 5)
|
||||
#define FUNC_MASK (3 << 5)
|
||||
|
||||
#define PULL_NONE (0 << 7)
|
||||
#if GPIO_VERSION == 0
|
||||
#define PULL_UP (1 << 7)
|
||||
#define PULL_DOWN (2 << 7)
|
||||
#else
|
||||
#define PULL_UP (3 << 7)
|
||||
#if GPIO_VERSION > 4
|
||||
#define PULL_UP_STRONG (2 << 7)
|
||||
#endif
|
||||
#define PULL_DOWN (1 << 7)
|
||||
#endif
|
||||
#define PULL_MASK (3 << 7)
|
||||
|
||||
#define INPUT_ENABLE (1 << 9)
|
||||
|
||||
#if GPIO_VERSION < 5
|
||||
#define DRIVE_X1 (0 << 10)
|
||||
#define DRIVE_X2 (1 << 10)
|
||||
#if GPIO_VERSION < 2
|
||||
#define DRIVE_X3 (2 << 10)
|
||||
#define DRIVE_X4 (3 << 10)
|
||||
#define DRIVE_X6 DRIVE_X4
|
||||
#else
|
||||
#define DRIVE_X3 DRIVE_X2
|
||||
#define DRIVE_X4 (2 << 10)
|
||||
#define DRIVE_X6 (3 << 10)
|
||||
#endif
|
||||
#else
|
||||
#define DRIVE_S0 (0 << 10 | 0 << 22)
|
||||
#define DRIVE_S1 (1 << 10 | 0 << 22)
|
||||
#define DRIVE_S2 (2 << 10 | 0 << 22)
|
||||
#define DRIVE_S3 (3 << 10 | 0 << 22)
|
||||
#define DRIVE_S4 (0 << 10 | 1 << 22)
|
||||
#define DRIVE_S5 (1 << 10 | 1 << 22)
|
||||
#define DRIVE_S6 (2 << 10 | 1 << 22)
|
||||
#define DRIVE_S7 (3 << 10 | 1 << 22)
|
||||
#define DRIVE_S8 (0 << 10 | 2 << 22)
|
||||
#define DRIVE_S9 (1 << 10 | 2 << 22)
|
||||
#define DRIVE_S10 (2 << 10 | 2 << 22)
|
||||
#define DRIVE_S11 (3 << 10 | 2 << 22)
|
||||
#define DRIVE_S12 (0 << 10 | 3 << 22)
|
||||
#define DRIVE_S13 (1 << 10 | 3 << 22)
|
||||
#define DRIVE_S14 (2 << 10 | 3 << 22)
|
||||
#define DRIVE_S15 (3 << 10 | 3 << 22)
|
||||
#endif
|
||||
|
||||
#if GPIO_VERSION < 3
|
||||
#define BUS_HOLD (1 << 12)
|
||||
#endif
|
||||
|
||||
#if GPIO_VERSION < 5
|
||||
#define FAST_SLEW (0 << 13)
|
||||
#define SLOW_SLEW (1 << 13)
|
||||
#else
|
||||
#define VERY_FAST_SLEW (0 << 15 | 0 << 13)
|
||||
#define FAST_SLEW (0 << 15 | 1 << 13)
|
||||
#define SLOW_SLEW (1 << 15 | 0 << 13)
|
||||
#endif
|
||||
|
||||
#if GPIO_VERSION > 3
|
||||
#define INPUT_CMOS (0 << 14)
|
||||
#define INPUT_SCHMITT (1 << 14)
|
||||
#endif
|
||||
|
||||
#if GPIO_VERSION > 2
|
||||
#define INTR_GRP_SEL0 (0 << 16)
|
||||
#define INTR_GRP_SEL1 (1 << 16)
|
||||
#define INTR_GRP_SEL2 (2 << 16)
|
||||
#define INTR_GRP_SEL3 (3 << 16)
|
||||
#define INTR_GRP_SEL4 (4 << 16)
|
||||
#define INTR_GRP_SEL5 (5 << 16)
|
||||
#define INTR_GRP_SEL6 (6 << 16)
|
||||
#define INT_MASKED (7 << 16)
|
||||
#endif
|
||||
|
||||
#define CFG_DISABLED ( FUNC_GPIO | CFG_DISABLE | INT_MASKED)
|
||||
#define CFG_IN (INPUT_ENABLE | FUNC_GPIO | CFG_GP_IN | INT_MASKED)
|
||||
#define CFG_OUT (INPUT_ENABLE | FUNC_GPIO | CFG_GP_OUT | INT_MASKED)
|
||||
#define CFG_OUT_0 (INPUT_ENABLE | FUNC_GPIO | CFG_GP_OUT | DATA_0 | INT_MASKED)
|
||||
#define CFG_OUT_1 (INPUT_ENABLE | FUNC_GPIO | CFG_GP_OUT | DATA_1 | INT_MASKED)
|
||||
#define CFG_FUNC0 (INPUT_ENABLE | FUNC_ALT0 | INT_MASKED)
|
||||
#define CFG_FUNC1 (INPUT_ENABLE | FUNC_ALT1 | INT_MASKED)
|
||||
#define CFG_FUNC2 (INPUT_ENABLE | FUNC_ALT2 | INT_MASKED)
|
||||
|
||||
/* Global slew rate adjustment, for hardware which supports it */
|
||||
|
||||
#define SLEW_RATE_FAST 0
|
||||
#define SLEW_RATE_SLOW 1
|
||||
|
||||
#endif /* ! __APPLE_GPIO_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2007 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
OPTIONS += \
|
||||
WITH_HW_GPIO=1
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/gpio.o
|
||||
|
||||
GLOBAL_INCLUDES += \
|
||||
$(LOCAL_DIR)/include
|
||||
Reference in new issue
Block a user