Initial commit
This commit is contained in:
342
target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c
Normal file
342
target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c
Normal file
@@ -0,0 +1,342 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/mach-rtl838x/mach-rtl83xx.h>
|
||||
|
||||
/* RTL8231 registers for LED control */
|
||||
#define RTL8231_LED_FUNC0 0x0000
|
||||
#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4))
|
||||
#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4))
|
||||
#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4))
|
||||
|
||||
#define USEC_TIMEOUT 5000
|
||||
|
||||
struct rtl8231_gpios {
|
||||
struct gpio_chip gc;
|
||||
struct device *dev;
|
||||
u32 id;
|
||||
int smi_bus_id;
|
||||
u16 reg_shadow[0x20];
|
||||
u32 reg_cached;
|
||||
int ext_gpio_indrt_access;
|
||||
};
|
||||
|
||||
extern struct mutex smi_lock;
|
||||
extern struct rtl83xx_soc_info soc_info;
|
||||
|
||||
static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg)
|
||||
{
|
||||
u32 t = 0, n = 0;
|
||||
u8 bus_id = gpios->smi_bus_id;
|
||||
|
||||
reg &= 0x1f;
|
||||
bus_id &= 0x1f;
|
||||
|
||||
/* Calculate read register address */
|
||||
t = (bus_id << 2) | (reg << 7);
|
||||
|
||||
/* Set execution bit: cleared when operation completed */
|
||||
t |= 1;
|
||||
|
||||
// Start execution
|
||||
sw_w32(t, gpios->ext_gpio_indrt_access);
|
||||
do {
|
||||
udelay(1);
|
||||
t = sw_r32(gpios->ext_gpio_indrt_access);
|
||||
n++;
|
||||
} while ((t & 1) && (n < USEC_TIMEOUT));
|
||||
|
||||
if (n >= USEC_TIMEOUT)
|
||||
return 0x80000000;
|
||||
|
||||
pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, (t & 0xffff0000) >> 16);
|
||||
|
||||
return (t & 0xffff0000) >> 16;
|
||||
}
|
||||
|
||||
static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data)
|
||||
{
|
||||
u32 t = 0, n = 0;
|
||||
u8 bus_id = gpios->smi_bus_id;
|
||||
|
||||
pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, data);
|
||||
reg &= 0x1f;
|
||||
bus_id &= 0x1f;
|
||||
|
||||
t = (bus_id << 2) | (reg << 7) | (data << 16);
|
||||
/* Set write bit */
|
||||
t |= 2;
|
||||
|
||||
/* Set execution bit: cleared when operation completed */
|
||||
t |= 1;
|
||||
|
||||
// Start execution
|
||||
sw_w32(t, gpios->ext_gpio_indrt_access);
|
||||
do {
|
||||
udelay(1);
|
||||
t = sw_r32(gpios->ext_gpio_indrt_access);
|
||||
} while ((t & 1) && (n < USEC_TIMEOUT));
|
||||
|
||||
if (n >= USEC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 rtl8231_read_cached(struct rtl8231_gpios *gpios, u32 reg)
|
||||
{
|
||||
if (reg > 0x1f)
|
||||
return 0;
|
||||
|
||||
if (gpios->reg_cached & (1 << reg))
|
||||
return gpios->reg_shadow[reg];
|
||||
|
||||
return rtl8231_read(gpios, reg);
|
||||
}
|
||||
|
||||
/* Set Direction of the RTL8231 pin:
|
||||
* dir 1: input
|
||||
* dir 0: output
|
||||
*/
|
||||
static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir)
|
||||
{
|
||||
u32 v;
|
||||
int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio);
|
||||
int pin_dir_addr = RTL8231_GPIO_DIR(gpio);
|
||||
int dpin = gpio % 16;
|
||||
|
||||
if (gpio > 31) {
|
||||
pr_debug("WARNING: HIGH pin\n");
|
||||
dpin += 5;
|
||||
pin_dir_addr = pin_sel_addr;
|
||||
}
|
||||
|
||||
v = rtl8231_read_cached(gpios, pin_dir_addr);
|
||||
if (v & 0x80000000) {
|
||||
pr_err("Error reading RTL8231\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = (v & ~(1 << dpin)) | (dir << dpin);
|
||||
rtl8231_write(gpios, pin_dir_addr, v);
|
||||
gpios->reg_shadow[pin_dir_addr] = v;
|
||||
gpios->reg_cached |= 1 << pin_dir_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8231_pin_dir_get(struct rtl8231_gpios *gpios, u32 gpio, u32 *dir)
|
||||
{
|
||||
/* dir 1: input
|
||||
* dir 0: output
|
||||
*/
|
||||
|
||||
u32 v;
|
||||
int pin_dir_addr = RTL8231_GPIO_DIR(gpio);
|
||||
int pin = gpio % 16;
|
||||
|
||||
if (gpio > 31) {
|
||||
pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio);
|
||||
pin += 5;
|
||||
}
|
||||
|
||||
v = rtl8231_read(gpios, pin_dir_addr);
|
||||
if (v & (1 << pin))
|
||||
*dir = 1;
|
||||
else
|
||||
*dir = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8231_pin_set(struct rtl8231_gpios *gpios, u32 gpio, u32 data)
|
||||
{
|
||||
u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio));
|
||||
|
||||
pr_debug("%s: %d to %d\n", __func__, gpio, data);
|
||||
if (v & 0x80000000) {
|
||||
pr_err("Error reading RTL8231\n");
|
||||
return -1;
|
||||
}
|
||||
v = (v & ~(1 << (gpio % 16))) | (data << (gpio % 16));
|
||||
rtl8231_write(gpios, RTL8231_GPIO_DATA(gpio), v);
|
||||
gpios->reg_shadow[RTL8231_GPIO_DATA(gpio)] = v;
|
||||
gpios->reg_cached |= 1 << RTL8231_GPIO_DATA(gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8231_pin_get(struct rtl8231_gpios *gpios, u32 gpio, u16 *state)
|
||||
{
|
||||
u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio));
|
||||
|
||||
if (v & 0x80000000) {
|
||||
pr_err("Error reading RTL8231\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*state = v & 0xffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8231_direction_input(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
int err;
|
||||
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
mutex_lock(&smi_lock);
|
||||
err = rtl8231_pin_dir(gpios, offset, 1);
|
||||
mutex_unlock(&smi_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rtl8231_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
int err;
|
||||
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
mutex_lock(&smi_lock);
|
||||
err = rtl8231_pin_dir(gpios, offset, 0);
|
||||
mutex_unlock(&smi_lock);
|
||||
if (!err)
|
||||
err = rtl8231_pin_set(gpios, offset, value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rtl8231_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
u32 v = 0;
|
||||
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
mutex_lock(&smi_lock);
|
||||
rtl8231_pin_dir_get(gpios, offset, &v);
|
||||
mutex_unlock(&smi_lock);
|
||||
return v;
|
||||
}
|
||||
|
||||
static int rtl8231_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
u16 state = 0;
|
||||
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&smi_lock);
|
||||
rtl8231_pin_get(gpios, offset, &state);
|
||||
mutex_unlock(&smi_lock);
|
||||
if (state & (1 << (offset % 16)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtl8231_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
rtl8231_pin_set(gpios, offset, value);
|
||||
}
|
||||
|
||||
int rtl8231_init(struct rtl8231_gpios *gpios)
|
||||
{
|
||||
pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id);
|
||||
|
||||
gpios->reg_cached = 0;
|
||||
|
||||
if (soc_info.family == RTL8390_FAMILY_ID) {
|
||||
// RTL8390: Enable external gpio in global led control register
|
||||
sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL);
|
||||
} else if (soc_info.family == RTL8380_FAMILY_ID) {
|
||||
// RTL8380: Enable RTL8231 indirect access mode
|
||||
sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL);
|
||||
sw_w32_mask(3, 1, RTL838X_DMY_REG5);
|
||||
}
|
||||
|
||||
/* Select GPIO functionality and force input direction for pins 0-36 */
|
||||
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff);
|
||||
rtl8231_write(gpios, RTL8231_GPIO_DIR(0), 0xffff);
|
||||
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff);
|
||||
rtl8231_write(gpios, RTL8231_GPIO_DIR(16), 0xffff);
|
||||
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(32), 0x03ff);
|
||||
|
||||
/* Set LED_Start to enable drivers for output mode */
|
||||
rtl8231_write(gpios, RTL8231_LED_FUNC0, 1 << 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rtl8231_gpio_of_match[] = {
|
||||
{ .compatible = "realtek,rtl8231-gpio" },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rtl8231_gpio_of_match);
|
||||
|
||||
static int rtl8231_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct rtl8231_gpios *gpios;
|
||||
int err;
|
||||
u32 indirect_bus_id;
|
||||
|
||||
pr_info("Probing RTL8231 GPIOs\n");
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "No DT found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
|
||||
if (!gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
gpios->id = soc_info.id;
|
||||
if (soc_info.family == RTL8380_FAMILY_ID) {
|
||||
gpios->ext_gpio_indrt_access = RTL838X_EXT_GPIO_INDRT_ACCESS;
|
||||
}
|
||||
|
||||
if (soc_info.family == RTL8390_FAMILY_ID) {
|
||||
gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use a default MDIO bus ID for the 8231 of 0, which can be overriden
|
||||
* by the indirect-access-bus-id property in the dts.
|
||||
*/
|
||||
gpios->smi_bus_id = 0;
|
||||
of_property_read_u32(np, "indirect-access-bus-id", &indirect_bus_id);
|
||||
gpios->smi_bus_id = indirect_bus_id;
|
||||
|
||||
rtl8231_init(gpios);
|
||||
|
||||
gpios->dev = dev;
|
||||
gpios->gc.base = 160;
|
||||
gpios->gc.ngpio = 36;
|
||||
gpios->gc.label = "rtl8231";
|
||||
gpios->gc.parent = dev;
|
||||
gpios->gc.owner = THIS_MODULE;
|
||||
gpios->gc.can_sleep = true;
|
||||
|
||||
gpios->gc.direction_input = rtl8231_direction_input;
|
||||
gpios->gc.direction_output = rtl8231_direction_output;
|
||||
gpios->gc.set = rtl8231_gpio_set;
|
||||
gpios->gc.get = rtl8231_gpio_get;
|
||||
gpios->gc.get_direction = rtl8231_get_direction;
|
||||
|
||||
err = devm_gpiochip_add_data(dev, &gpios->gc, gpios);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver rtl8231_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "rtl8231-gpio",
|
||||
.of_match_table = rtl8231_gpio_of_match,
|
||||
},
|
||||
.probe = rtl8231_gpio_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(rtl8231_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Realtek RTL8231 GPIO expansion chip support");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
425
target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c
Normal file
425
target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl838x.c
Normal file
@@ -0,0 +1,425 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/mach-rtl838x/mach-rtl83xx.h>
|
||||
|
||||
/* RTL8231 registers for LED control */
|
||||
#define RTL8231_LED_FUNC0 0x0000
|
||||
#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4))
|
||||
#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4))
|
||||
#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4))
|
||||
|
||||
struct rtl838x_gpios {
|
||||
struct gpio_chip gc;
|
||||
u32 id;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
int num_leds;
|
||||
int min_led;
|
||||
int leds_per_port;
|
||||
u32 led_mode;
|
||||
int led_glb_ctrl;
|
||||
int led_sw_ctrl;
|
||||
int (*led_sw_p_ctrl)(int port);
|
||||
int (*led_sw_p_en_ctrl)(int port);
|
||||
int (*ext_gpio_dir)(int i);
|
||||
int (*ext_gpio_data)(int i);
|
||||
};
|
||||
|
||||
inline int rtl838x_ext_gpio_dir(int i)
|
||||
{
|
||||
return RTL838X_EXT_GPIO_DIR + ((i >>5) << 2);
|
||||
}
|
||||
|
||||
inline int rtl839x_ext_gpio_dir(int i)
|
||||
{
|
||||
return RTL839X_EXT_GPIO_DIR + ((i >>5) << 2);
|
||||
}
|
||||
|
||||
inline int rtl838x_ext_gpio_data(int i)
|
||||
{
|
||||
return RTL838X_EXT_GPIO_DATA + ((i >>5) << 2);
|
||||
}
|
||||
|
||||
inline int rtl839x_ext_gpio_data(int i)
|
||||
{
|
||||
return RTL839X_EXT_GPIO_DATA + ((i >>5) << 2);
|
||||
}
|
||||
|
||||
inline int rtl838x_led_sw_p_ctrl(int p)
|
||||
{
|
||||
return RTL838X_LED_SW_P_CTRL + (p << 2);
|
||||
}
|
||||
|
||||
inline int rtl839x_led_sw_p_ctrl(int p)
|
||||
{
|
||||
return RTL839X_LED_SW_P_CTRL + (p << 2);
|
||||
}
|
||||
|
||||
inline int rtl838x_led_sw_p_en_ctrl(int p)
|
||||
{
|
||||
return RTL838X_LED_SW_P_EN_CTRL + ((p / 10) << 2);
|
||||
}
|
||||
|
||||
inline int rtl839x_led_sw_p_en_ctrl(int p)
|
||||
{
|
||||
return RTL839X_LED_SW_P_EN_CTRL + ((p / 10) << 2);
|
||||
}
|
||||
|
||||
extern struct mutex smi_lock;
|
||||
extern struct rtl83xx_soc_info soc_info;
|
||||
|
||||
|
||||
void rtl838x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
int bit;
|
||||
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
pr_debug("rtl838x_set: %d, value: %d\n", offset, value);
|
||||
/* Internal GPIO of the RTL8380 */
|
||||
if (offset < 32) {
|
||||
if (value)
|
||||
rtl83xx_w32_mask(0, BIT(offset), RTL838X_GPIO_PABC_DATA);
|
||||
else
|
||||
rtl83xx_w32_mask(BIT(offset), 0, RTL838X_GPIO_PABC_DATA);
|
||||
}
|
||||
|
||||
/* LED driver for PWR and SYS */
|
||||
if (offset >= 32 && offset < 64) {
|
||||
bit = offset - 32;
|
||||
if (value)
|
||||
sw_w32_mask(0, BIT(bit), gpios->led_glb_ctrl);
|
||||
else
|
||||
sw_w32_mask(BIT(bit), 0, gpios->led_glb_ctrl);
|
||||
return;
|
||||
}
|
||||
|
||||
bit = (offset - 64) % 32;
|
||||
/* First Port-LED */
|
||||
if (offset >= 64 && offset < 96
|
||||
&& offset >= (64 + gpios->min_led)
|
||||
&& offset < (64 + gpios->min_led + gpios->num_leds)) {
|
||||
if (value)
|
||||
sw_w32_mask(7, 5, gpios->led_sw_p_ctrl(bit));
|
||||
else
|
||||
sw_w32_mask(7, 0, gpios->led_sw_p_ctrl(bit));
|
||||
}
|
||||
if (offset >= 96 && offset < 128
|
||||
&& offset >= (96 + gpios->min_led)
|
||||
&& offset < (96 + gpios->min_led + gpios->num_leds)) {
|
||||
if (value)
|
||||
sw_w32_mask(7 << 3, 5 << 3, gpios->led_sw_p_ctrl(bit));
|
||||
else
|
||||
sw_w32_mask(7 << 3, 0, gpios->led_sw_p_ctrl(bit));
|
||||
}
|
||||
if (offset >= 128 && offset < 160
|
||||
&& offset >= (128 + gpios->min_led)
|
||||
&& offset < (128 + gpios->min_led + gpios->num_leds)) {
|
||||
if (value)
|
||||
sw_w32_mask(7 << 6, 5 << 6, gpios->led_sw_p_ctrl(bit));
|
||||
else
|
||||
sw_w32_mask(7 << 6, 0, gpios->led_sw_p_ctrl(bit));
|
||||
}
|
||||
__asm__ volatile ("sync");
|
||||
}
|
||||
|
||||
static int rtl838x_direction_input(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
|
||||
if (offset < 32) {
|
||||
rtl83xx_w32_mask(BIT(offset), 0, RTL838X_GPIO_PABC_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Internal LED driver does not support input */
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int rtl838x_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
if (offset < 32)
|
||||
rtl83xx_w32_mask(0, BIT(offset), RTL838X_GPIO_PABC_DIR);
|
||||
rtl838x_gpio_set(gc, offset, value);
|
||||
|
||||
/* LED for PWR and SYS driver is direction output by default */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl838x_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
u32 v = 0;
|
||||
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
if (offset < 32) {
|
||||
v = rtl83xx_r32(RTL838X_GPIO_PABC_DIR);
|
||||
if (v & BIT(offset))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* LED driver for PWR and SYS is direction output by default */
|
||||
if (offset >= 32 && offset < 64)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl838x_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
u32 v;
|
||||
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
||||
|
||||
pr_debug("%s: %d\n", __func__, offset);
|
||||
|
||||
/* Internal GPIO of the RTL8380 */
|
||||
if (offset < 32) {
|
||||
v = rtl83xx_r32(RTL838X_GPIO_PABC_DATA);
|
||||
if (v & BIT(offset))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LED driver for PWR and SYS */
|
||||
if (offset >= 32 && offset < 64) {
|
||||
v = sw_r32(gpios->led_glb_ctrl);
|
||||
if (v & BIT(offset-32))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* BUG:
|
||||
bit = (offset - 64) % 32;
|
||||
if (offset >= 64 && offset < 96) {
|
||||
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & BIT(bit))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
if (offset >= 96 && offset < 128) {
|
||||
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & BIT(bit))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
if (offset >= 128 && offset < 160) {
|
||||
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & BIT(bit))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtl8380_led_test(struct rtl838x_gpios *gpios, u32 mask)
|
||||
{
|
||||
int i;
|
||||
u32 led_gbl = sw_r32(gpios->led_glb_ctrl);
|
||||
u32 mode_sel, led_p_en;
|
||||
|
||||
if (soc_info.family == RTL8380_FAMILY_ID) {
|
||||
mode_sel = sw_r32(RTL838X_LED_MODE_SEL);
|
||||
led_p_en = sw_r32(RTL838X_LED_P_EN_CTRL);
|
||||
}
|
||||
|
||||
/* 2 Leds for ports 0-23 and 24-27, 3 would be 0x7 */
|
||||
sw_w32_mask(0x3f, 0x3 | (0x3 << 3), gpios->led_glb_ctrl);
|
||||
|
||||
if(soc_info.family == RTL8380_FAMILY_ID) {
|
||||
/* Enable all leds */
|
||||
sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL);
|
||||
}
|
||||
/* Enable software control of all leds */
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_ctrl);
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_p_en_ctrl(0));
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_p_en_ctrl(10));
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(20));
|
||||
|
||||
for (i = 0; i < 28; i++) {
|
||||
if (mask & BIT(i))
|
||||
sw_w32(5 | (5 << 3) | (5 << 6), gpios->led_sw_p_ctrl(i));
|
||||
}
|
||||
msleep(3000);
|
||||
|
||||
if (soc_info.family == RTL8380_FAMILY_ID)
|
||||
sw_w32(led_p_en, RTL838X_LED_P_EN_CTRL);
|
||||
/* Disable software control of all leds */
|
||||
sw_w32(0x0000000, gpios->led_sw_ctrl);
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(0));
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(10));
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(20));
|
||||
|
||||
sw_w32(led_gbl, gpios->led_glb_ctrl);
|
||||
if (soc_info.family == RTL8380_FAMILY_ID)
|
||||
sw_w32(mode_sel, RTL838X_LED_MODE_SEL);
|
||||
}
|
||||
|
||||
void take_port_leds(struct rtl838x_gpios *gpios)
|
||||
{
|
||||
int leds_per_port = gpios->leds_per_port;
|
||||
int mode = gpios->led_mode;
|
||||
|
||||
pr_info("%s, %d, %x\n", __func__, leds_per_port, mode);
|
||||
pr_debug("Bootloader settings: %x %x %x\n",
|
||||
sw_r32(gpios->led_sw_p_en_ctrl(0)),
|
||||
sw_r32(gpios->led_sw_p_en_ctrl(10)),
|
||||
sw_r32(gpios->led_sw_p_en_ctrl(20))
|
||||
);
|
||||
|
||||
if (soc_info.family == RTL8380_FAMILY_ID) {
|
||||
pr_debug("led glb: %x, sel %x\n",
|
||||
sw_r32(gpios->led_glb_ctrl), sw_r32(RTL838X_LED_MODE_SEL));
|
||||
pr_debug("RTL838X_LED_P_EN_CTRL: %x", sw_r32(RTL838X_LED_P_EN_CTRL));
|
||||
pr_debug("RTL838X_LED_MODE_CTRL: %x", sw_r32(RTL838X_LED_MODE_CTRL));
|
||||
sw_w32_mask(3, 0, RTL838X_LED_MODE_SEL);
|
||||
sw_w32(mode, RTL838X_LED_MODE_CTRL);
|
||||
}
|
||||
|
||||
/* Enable software control of all leds */
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_ctrl);
|
||||
if (soc_info.family == RTL8380_FAMILY_ID)
|
||||
sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL);
|
||||
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(0));
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(10));
|
||||
sw_w32(0x0000000, gpios->led_sw_p_en_ctrl(20));
|
||||
|
||||
sw_w32_mask(0x3f, 0, gpios->led_glb_ctrl);
|
||||
switch (leds_per_port) {
|
||||
case 3:
|
||||
sw_w32_mask(0, 0x7 | (0x7 << 3), gpios->led_glb_ctrl);
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_p_en_ctrl(20));
|
||||
/* FALLTHRU */
|
||||
case 2:
|
||||
sw_w32_mask(0, 0x3 | (0x3 << 3), gpios->led_glb_ctrl);
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_p_en_ctrl(10));
|
||||
/* FALLTHRU */
|
||||
case 1:
|
||||
sw_w32_mask(0, 0x1 | (0x1 << 3), gpios->led_glb_ctrl);
|
||||
sw_w32(0xFFFFFFF, gpios->led_sw_p_en_ctrl(0));
|
||||
break;
|
||||
default:
|
||||
pr_err("No LEDS configured for software control\n");
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id rtl838x_gpio_of_match[] = {
|
||||
{ .compatible = "realtek,rtl838x-gpio" },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rtl838x_gpio_of_match);
|
||||
|
||||
static int rtl838x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct rtl838x_gpios *gpios;
|
||||
int err;
|
||||
|
||||
pr_info("Probing RTL838X GPIOs\n");
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "No DT found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
|
||||
if (!gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
gpios->id = soc_info.id;
|
||||
|
||||
switch (gpios->id) {
|
||||
case 0x8332:
|
||||
pr_debug("Found RTL8332M GPIO\n");
|
||||
break;
|
||||
case 0x8380:
|
||||
pr_debug("Found RTL8380M GPIO\n");
|
||||
break;
|
||||
case 0x8381:
|
||||
pr_debug("Found RTL8381M GPIO\n");
|
||||
break;
|
||||
case 0x8382:
|
||||
pr_debug("Found RTL8382M GPIO\n");
|
||||
break;
|
||||
case 0x8391:
|
||||
pr_debug("Found RTL8391 GPIO\n");
|
||||
break;
|
||||
case 0x8393:
|
||||
pr_debug("Found RTL8393 GPIO\n");
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown GPIO chip id (%04x)\n", gpios->id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (soc_info.family == RTL8380_FAMILY_ID) {
|
||||
gpios->led_glb_ctrl = RTL838X_LED_GLB_CTRL;
|
||||
gpios->led_sw_ctrl = RTL838X_LED_SW_CTRL;
|
||||
gpios->led_sw_p_ctrl = rtl838x_led_sw_p_ctrl;
|
||||
gpios->led_sw_p_en_ctrl = rtl838x_led_sw_p_en_ctrl;
|
||||
gpios->ext_gpio_dir = rtl838x_ext_gpio_dir;
|
||||
gpios->ext_gpio_data = rtl838x_ext_gpio_data;
|
||||
}
|
||||
|
||||
if (soc_info.family == RTL8390_FAMILY_ID) {
|
||||
gpios->led_glb_ctrl = RTL839X_LED_GLB_CTRL;
|
||||
gpios->led_sw_ctrl = RTL839X_LED_SW_CTRL;
|
||||
gpios->led_sw_p_ctrl = rtl839x_led_sw_p_ctrl;
|
||||
gpios->led_sw_p_en_ctrl = rtl839x_led_sw_p_en_ctrl;
|
||||
gpios->ext_gpio_dir = rtl839x_ext_gpio_dir;
|
||||
gpios->ext_gpio_data = rtl839x_ext_gpio_data;
|
||||
}
|
||||
|
||||
gpios->dev = dev;
|
||||
gpios->gc.base = 0;
|
||||
/* 0-31: internal
|
||||
* 32-63, LED control register
|
||||
* 64-95: PORT-LED 0
|
||||
* 96-127: PORT-LED 1
|
||||
* 128-159: PORT-LED 2
|
||||
*/
|
||||
gpios->gc.ngpio = 160;
|
||||
gpios->gc.label = "rtl838x";
|
||||
gpios->gc.parent = dev;
|
||||
gpios->gc.owner = THIS_MODULE;
|
||||
gpios->gc.can_sleep = true;
|
||||
gpios->irq = 31;
|
||||
|
||||
gpios->gc.direction_input = rtl838x_direction_input;
|
||||
gpios->gc.direction_output = rtl838x_direction_output;
|
||||
gpios->gc.set = rtl838x_gpio_set;
|
||||
gpios->gc.get = rtl838x_gpio_get;
|
||||
gpios->gc.get_direction = rtl838x_get_direction;
|
||||
|
||||
if (of_property_read_bool(np, "take-port-leds")) {
|
||||
if (of_property_read_u32(np, "leds-per-port", &gpios->leds_per_port))
|
||||
gpios->leds_per_port = 2;
|
||||
if (of_property_read_u32(np, "led-mode", &gpios->led_mode))
|
||||
gpios->led_mode = (0x1ea << 15) | 0x1ea;
|
||||
if (of_property_read_u32(np, "num-leds", &gpios->num_leds))
|
||||
gpios->num_leds = 32;
|
||||
if (of_property_read_u32(np, "min-led", &gpios->min_led))
|
||||
gpios->min_led = 0;
|
||||
take_port_leds(gpios);
|
||||
}
|
||||
|
||||
err = devm_gpiochip_add_data(dev, &gpios->gc, gpios);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver rtl838x_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "rtl838x-gpio",
|
||||
.of_match_table = rtl838x_gpio_of_match,
|
||||
},
|
||||
.probe = rtl838x_gpio_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(rtl838x_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Realtek RTL838X GPIO API support");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Reference in New Issue
Block a user