mirror of
https://github.com/openwrt/openwrt.git
synced 2025-01-01 11:36:49 +00:00
d5c4750a71
Do some minor empty lines cleanup, i.e. remove those at EOF and add some for cosmetic reasons/consistency. Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
809 lines
20 KiB
C
809 lines
20 KiB
C
// 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-rtl838x.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 RTL8231_GPIO_PIN_SEL0 0x0002
|
|
#define RTL8231_GPIO_PIN_SEL1 0x0003
|
|
#define RTL8231_GPIO_PIN_SEL2 0x0004
|
|
#define RTL8231_GPIO_IO_SEL0 0x0005
|
|
#define RTL8231_GPIO_IO_SEL1 0x0006
|
|
#define RTL8231_GPIO_IO_SEL2 0x0007
|
|
|
|
#define MDC_WAIT { int i; for (i = 0; i < 2; i++); }
|
|
#define I2C_WAIT { int i; for (i = 0; i < 5; i++); }
|
|
|
|
struct rtl838x_gpios {
|
|
struct gpio_chip gc;
|
|
u32 id;
|
|
struct device *dev;
|
|
int irq;
|
|
int bus_id;
|
|
int num_leds;
|
|
int min_led;
|
|
int leds_per_port;
|
|
u32 led_mode;
|
|
u16 rtl8381_phy_id;
|
|
int smi_clock;
|
|
int smi_data;
|
|
int i2c_sda;
|
|
int i2c_sdc;
|
|
};
|
|
|
|
extern struct mutex smi_lock;
|
|
|
|
u32 rtl838x_rtl8231_read(u8 bus_id, u32 reg)
|
|
{
|
|
u32 t = 0;
|
|
|
|
reg &= 0x1f;
|
|
bus_id &= 0x1f;
|
|
|
|
/* Calculate read register address */
|
|
t = (bus_id << 2) | (reg << 7);
|
|
|
|
mutex_lock(&smi_lock);
|
|
/* Set execution bit: cleared when operation completed */
|
|
t |= 1;
|
|
sw_w32(t, RTL838X_EXT_GPIO_INDRT_ACCESS);
|
|
do { /* TODO: Return 0x80000000 if timeout */
|
|
t = sw_r32(RTL838X_EXT_GPIO_INDRT_ACCESS);
|
|
} while (t & 1);
|
|
pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, (t & 0xffff0000) >> 16);
|
|
|
|
mutex_unlock(&smi_lock);
|
|
return (t & 0xffff0000) >> 16;
|
|
}
|
|
|
|
int rtl838x_rtl8231_write(u8 bus_id, u32 reg, u32 data)
|
|
{
|
|
u32 t = 0;
|
|
|
|
pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, data);
|
|
data &= 0xffff;
|
|
reg &= 0x1f;
|
|
bus_id &= 0x1f;
|
|
|
|
mutex_lock(&smi_lock);
|
|
t = (bus_id << 2) | (reg << 7) | (data << 16);
|
|
/* Set write bit */
|
|
t |= 2;
|
|
|
|
/* Set execution bit: cleared when operation completed */
|
|
t |= 1;
|
|
sw_w32(t, RTL838X_EXT_GPIO_INDRT_ACCESS);
|
|
do { /* TODO: Return -1 if timeout */
|
|
t = sw_r32(RTL838X_EXT_GPIO_INDRT_ACCESS);
|
|
} while (t & 1);
|
|
|
|
mutex_unlock(&smi_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int rtl8231_pin_dir(u8 bus_id, u32 gpio, u32 dir)
|
|
{
|
|
/* dir 1: input
|
|
* dir 0: output
|
|
*/
|
|
|
|
u32 v;
|
|
int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio);
|
|
int pin_dir_addr = RTL8231_GPIO_DIR(gpio);
|
|
int pin = gpio % 16;
|
|
int dpin = pin;
|
|
|
|
if (gpio > 31) {
|
|
dpin = pin << 5;
|
|
pin_dir_addr = pin_sel_addr;
|
|
}
|
|
|
|
/* Select GPIO function for pin */
|
|
v = rtl838x_rtl8231_read(bus_id, pin_sel_addr);
|
|
if (v & 0x80000000) {
|
|
pr_err("Error reading RTL8231\n");
|
|
return -1;
|
|
}
|
|
rtl838x_rtl8231_write(bus_id, pin_sel_addr, v | (1 << pin));
|
|
|
|
v = rtl838x_rtl8231_read(bus_id, pin_dir_addr);
|
|
if (v & 0x80000000) {
|
|
pr_err("Error reading RTL8231\n");
|
|
return -1;
|
|
}
|
|
rtl838x_rtl8231_write(bus_id, pin_dir_addr,
|
|
(v & ~(1 << dpin)) | (dir << dpin));
|
|
return 0;
|
|
}
|
|
|
|
static int rtl8231_pin_dir_get(u8 bus_id, 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 = pin << 5;
|
|
}
|
|
|
|
v = rtl838x_rtl8231_read(bus_id, pin_dir_addr);
|
|
if (v & (1 << pin))
|
|
*dir = 1;
|
|
else
|
|
*dir = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int rtl8231_pin_set(u8 bus_id, u32 gpio, u32 data)
|
|
{
|
|
u32 v = rtl838x_rtl8231_read(bus_id, RTL8231_GPIO_DATA(gpio));
|
|
|
|
if (v & 0x80000000) {
|
|
pr_err("Error reading RTL8231\n");
|
|
return -1;
|
|
}
|
|
rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_DATA(gpio),
|
|
(v & ~(1 << (gpio % 16))) | (data << (gpio % 16)));
|
|
return 0;
|
|
}
|
|
|
|
static int rtl8231_pin_get(u8 bus_id, u32 gpio, u16 *state)
|
|
{
|
|
u32 v = rtl838x_rtl8231_read(bus_id, RTL8231_GPIO_DATA(gpio));
|
|
|
|
if (v & 0x80000000) {
|
|
pr_err("Error reading RTL8231\n");
|
|
return -1;
|
|
}
|
|
|
|
*state = v & 0xffff;
|
|
return 0;
|
|
}
|
|
|
|
static int rtl838x_direction_input(struct gpio_chip *gc, unsigned int offset)
|
|
{
|
|
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
|
|
|
pr_debug("%s: %d\n", __func__, offset);
|
|
|
|
if (offset < 32) {
|
|
rtl838x_w32_mask(1 << offset, 0, RTL838X_GPIO_PABC_DIR);
|
|
return 0;
|
|
}
|
|
|
|
/* Internal LED driver does not support input */
|
|
if (offset >= 32 && offset < 64)
|
|
return -ENOTSUPP;
|
|
|
|
if (offset >= 64 && offset < 100 && gpios->bus_id >= 0)
|
|
return rtl8231_pin_dir(gpios->bus_id, offset - 64, 1);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
static int rtl838x_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
|
|
{
|
|
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
|
|
|
pr_debug("%s: %d\n", __func__, offset);
|
|
if (offset < 32)
|
|
rtl838x_w32_mask(0, 1 << offset, RTL838X_GPIO_PABC_DIR);
|
|
|
|
/* LED for PWR and SYS driver is direction output by default */
|
|
if (offset >= 32 && offset < 64)
|
|
return 0;
|
|
|
|
if (offset >= 64 && offset < 100 && gpios->bus_id >= 0)
|
|
return rtl8231_pin_dir(gpios->bus_id, offset - 64, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int rtl838x_get_direction(struct gpio_chip *gc, unsigned int offset)
|
|
{
|
|
u32 v = 0;
|
|
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
|
|
|
pr_debug("%s: %d\n", __func__, offset);
|
|
if (offset < 32) {
|
|
v = rtl838x_r32(RTL838X_GPIO_PABC_DIR);
|
|
if (v & (1 << offset))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* LED driver for PWR and SYS is direction output by default */
|
|
if (offset >= 32 && offset < 64)
|
|
return 0;
|
|
|
|
if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) {
|
|
rtl8231_pin_dir_get(gpios->bus_id, offset - 64, &v);
|
|
return v;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtl838x_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
|
{
|
|
u32 v;
|
|
u16 state = 0;
|
|
int bit;
|
|
struct rtl838x_gpios *gpios = gpiochip_get_data(gc);
|
|
|
|
pr_debug("%s: %d\n", __func__, offset);
|
|
|
|
/* Internal GPIO of the RTL8380 */
|
|
if (offset < 32) {
|
|
v = rtl838x_r32(RTL838X_GPIO_PABC_DATA);
|
|
if (v & (1 << offset))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* LED driver for PWR and SYS */
|
|
if (offset >= 32 && offset < 64) {
|
|
v = sw_r32(RTL838X_LED_GLB_CTRL);
|
|
if (v & (1 << (offset-32)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Indirect access GPIO with RTL8231 */
|
|
if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) {
|
|
rtl8231_pin_get(gpios->bus_id, offset - 64, &state);
|
|
if (state & (1 << (offset % 16)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
bit = (offset - 100) % 32;
|
|
if (offset >= 100 && offset < 132) {
|
|
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (offset >= 132 && offset < 164) {
|
|
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (offset >= 164 && offset < 196) {
|
|
if (sw_r32(RTL838X_LED1_SW_P_EN_CTRL) & (1 << bit))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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)
|
|
rtl838x_w32_mask(0, 1 << offset, RTL838X_GPIO_PABC_DATA);
|
|
else
|
|
rtl838x_w32_mask(1 << 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, 1 << bit, RTL838X_LED_GLB_CTRL);
|
|
else
|
|
sw_w32_mask(1 << bit, 0, RTL838X_LED_GLB_CTRL);
|
|
return;
|
|
}
|
|
|
|
/* Indirect access GPIO with RTL8231 */
|
|
if (offset >= 64 && offset < 100 && gpios->bus_id >= 0) {
|
|
rtl8231_pin_set(gpios->bus_id, offset - 64, value);
|
|
return;
|
|
}
|
|
|
|
bit = (offset - 100) % 32;
|
|
/* First Port-LED */
|
|
if (offset >= 100 && offset < 132
|
|
&& offset >= (100 + gpios->min_led)
|
|
&& offset < (100 + gpios->min_led + gpios->num_leds)) {
|
|
if (value)
|
|
sw_w32_mask(7, 5, RTL838X_LED_SW_P_CTRL(bit));
|
|
else
|
|
sw_w32_mask(7, 0, RTL838X_LED_SW_P_CTRL(bit));
|
|
}
|
|
if (offset >= 132 && offset < 164
|
|
&& offset >= (132 + gpios->min_led)
|
|
&& offset < (132 + gpios->min_led + gpios->num_leds)) {
|
|
if (value)
|
|
sw_w32_mask(7 << 3, 5 << 3, RTL838X_LED_SW_P_CTRL(bit));
|
|
else
|
|
sw_w32_mask(7 << 3, 0, RTL838X_LED_SW_P_CTRL(bit));
|
|
}
|
|
if (offset >= 164 && offset < 196
|
|
&& offset >= (164 + gpios->min_led)
|
|
&& offset < (164 + gpios->min_led + gpios->num_leds)) {
|
|
if (value)
|
|
sw_w32_mask(7 << 6, 5 << 6, RTL838X_LED_SW_P_CTRL(bit));
|
|
else
|
|
sw_w32_mask(7 << 6, 0, RTL838X_LED_SW_P_CTRL(bit));
|
|
}
|
|
__asm__ volatile ("sync");
|
|
}
|
|
|
|
int rtl8231_init(struct rtl838x_gpios *gpios)
|
|
{
|
|
uint32_t v;
|
|
u8 bus_id = gpios->bus_id;
|
|
|
|
pr_info("%s called\n", __func__);
|
|
|
|
/* Enable RTL8231 indirect access mode */
|
|
sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL);
|
|
sw_w32_mask(3, 1, RTL838X_DMY_REG5);
|
|
|
|
/* Enable RTL8231 via GPIO_A1 line */
|
|
rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DIR);
|
|
rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DATA);
|
|
mdelay(50); /* wait 50ms for reset */
|
|
|
|
/*Select GPIO functionality for pins 0-15, 16-31 and 32-37 */
|
|
rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL(0), 0xffff);
|
|
rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL(16), 0xffff);
|
|
rtl838x_rtl8231_write(bus_id, RTL8231_GPIO_PIN_SEL2, 0x03ff);
|
|
|
|
v = rtl838x_rtl8231_read(bus_id, RTL8231_LED_FUNC0);
|
|
pr_info("RTL8231 led function now: %x\n", v);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void smi_write_bit(struct rtl838x_gpios *gpios, u32 bit)
|
|
{
|
|
if (bit)
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DATA);
|
|
else
|
|
rtl838x_w32_mask(1 << gpios->smi_data, 0, RTL838X_GPIO_PABC_DATA);
|
|
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA);
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA);
|
|
}
|
|
|
|
static int smi_read_bit(struct rtl838x_gpios *gpios)
|
|
{
|
|
u32 v;
|
|
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA);
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA);
|
|
|
|
v = rtl838x_r32(RTL838X_GPIO_PABC_DATA);
|
|
if (v & (1 << gpios->smi_data))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Tri-state of MDIO line */
|
|
static void smi_z(struct rtl838x_gpios *gpios)
|
|
{
|
|
/* MDIO pin to input */
|
|
rtl838x_w32_mask(1 << gpios->smi_data, 0, RTL838X_GPIO_PABC_DIR);
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(1 << gpios->smi_clock, 0, RTL838X_GPIO_PABC_DATA);
|
|
MDC_WAIT;
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DATA);
|
|
}
|
|
|
|
static void smi_write_bits(struct rtl838x_gpios *gpios, u32 data, int len)
|
|
{
|
|
while (len) {
|
|
len--;
|
|
smi_write_bit(gpios, data & (1 << len));
|
|
}
|
|
}
|
|
|
|
static void smi_read_bits(struct rtl838x_gpios *gpios, int len, u32 *data)
|
|
{
|
|
u32 v = 0;
|
|
|
|
while (len) {
|
|
len--;
|
|
v <<= 1;
|
|
v |= smi_read_bit(gpios);
|
|
}
|
|
*data = v;
|
|
}
|
|
|
|
/* Bit-banged verson of SMI write access, caller must hold smi_lock */
|
|
int rtl8380_smi_write(struct rtl838x_gpios *gpios, u16 reg, u32 data)
|
|
{
|
|
u16 bus_id = gpios->bus_id;
|
|
|
|
/* Set clock and data pins on RTL838X to output */
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DIR);
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DIR);
|
|
|
|
/* Write start bits */
|
|
smi_write_bits(gpios, 0xffffffff, 32);
|
|
|
|
smi_write_bits(gpios, 0x5, 4); /* ST and write OP */
|
|
|
|
smi_write_bits(gpios, bus_id, 5); /* 5 bits: phy address */
|
|
smi_write_bits(gpios, reg, 5); /* 5 bits: register address */
|
|
|
|
smi_write_bits(gpios, 0x2, 2); /* TURNAROUND */
|
|
|
|
smi_write_bits(gpios, data, 16); /* 16 bits: data*/
|
|
|
|
smi_z(gpios);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Bit-banged verson of SMI read access, caller must hold smi_lock */
|
|
int rtl8380_smi_read(struct rtl838x_gpios *gpios, u16 reg, u32 *data)
|
|
{
|
|
u16 bus_id = gpios->bus_id;
|
|
|
|
/* Set clock and data pins on RTL838X to output */
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_clock, RTL838X_GPIO_PABC_DIR);
|
|
rtl838x_w32_mask(0, 1 << gpios->smi_data, RTL838X_GPIO_PABC_DIR);
|
|
|
|
/* Write start bits */
|
|
smi_write_bits(gpios, 0xffffffff, 32);
|
|
|
|
smi_write_bits(gpios, 0x6, 4); /* ST and read OP */
|
|
|
|
smi_write_bits(gpios, bus_id, 5); /* 5 bits: phy address */
|
|
smi_write_bits(gpios, reg, 5); /* 5 bits: register address */
|
|
|
|
smi_z(gpios); /* TURNAROUND */
|
|
|
|
smi_read_bits(gpios, 16, data);
|
|
return 0;
|
|
}
|
|
|
|
static void i2c_pin_set(struct rtl838x_gpios *gpios, int pin, u32 data)
|
|
{
|
|
u32 v;
|
|
|
|
rtl8380_smi_read(gpios, RTL8231_GPIO_DATA(pin), &v);
|
|
if (!data)
|
|
v &= ~(1 << (pin % 16));
|
|
else
|
|
v |= (1 << (pin % 16));
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_DATA(pin), v);
|
|
}
|
|
|
|
static void i2c_pin_get(struct rtl838x_gpios *gpios, int pin, u32 *data)
|
|
{
|
|
u32 v;
|
|
|
|
rtl8380_smi_read(gpios, RTL8231_GPIO_DATA(pin), &v);
|
|
if (v & (1 << (pin % 16))) {
|
|
*data = 1;
|
|
return;
|
|
}
|
|
*data = 0;
|
|
}
|
|
|
|
static void i2c_pin_dir(struct rtl838x_gpios *gpios, int pin, u16 direction)
|
|
{
|
|
u32 v;
|
|
|
|
rtl8380_smi_read(gpios, RTL8231_GPIO_DIR(pin), &v);
|
|
if (direction) // Output
|
|
v &= ~(1 << (pin % 16));
|
|
else
|
|
v |= (1 << (pin % 16));
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_DIR(pin), v);
|
|
}
|
|
|
|
static void i2c_start(struct rtl838x_gpios *gpios)
|
|
{
|
|
i2c_pin_dir(gpios, gpios->i2c_sda, 0); /* Output */
|
|
i2c_pin_dir(gpios, gpios->i2c_sdc, 0); /* Output */
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 1);
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sda, 1);
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sda, 0);
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 0);
|
|
I2C_WAIT;
|
|
}
|
|
|
|
static void i2c_stop(struct rtl838x_gpios *gpios)
|
|
{
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 1);
|
|
i2c_pin_set(gpios, gpios->i2c_sda, 0);
|
|
I2C_WAIT;
|
|
|
|
i2c_pin_set(gpios, gpios->i2c_sda, 1);
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 0);
|
|
|
|
i2c_pin_dir(gpios, gpios->i2c_sda, 1); /* Input */
|
|
i2c_pin_dir(gpios, gpios->i2c_sdc, 1); /* Input */
|
|
}
|
|
|
|
static void i2c_read_bits(struct rtl838x_gpios *gpios, int len, u32 *data)
|
|
{
|
|
u32 v = 0, t;
|
|
|
|
while (len) {
|
|
len--;
|
|
v <<= 1;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 1);
|
|
I2C_WAIT;
|
|
i2c_pin_get(gpios, gpios->i2c_sda, &t);
|
|
v |= t;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 0);
|
|
I2C_WAIT;
|
|
}
|
|
*data = v;
|
|
}
|
|
|
|
static void i2c_write_bits(struct rtl838x_gpios *gpios, u32 data, int len)
|
|
{
|
|
while (len) {
|
|
len--;
|
|
i2c_pin_set(gpios, gpios->i2c_sda, data & (1 << len));
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 1);
|
|
I2C_WAIT;
|
|
i2c_pin_set(gpios, gpios->i2c_sdc, 0);
|
|
I2C_WAIT;
|
|
}
|
|
}
|
|
|
|
/* This initializes direct external GPIOs via the RTL8231 */
|
|
int rtl8380_rtl8321_init(struct rtl838x_gpios *gpios)
|
|
{
|
|
u32 v;
|
|
int mdc = gpios->smi_clock;
|
|
int mdio = gpios->smi_data;
|
|
|
|
pr_info("Configuring SMI: Clock %d, Data %d\n", mdc, mdio);
|
|
sw_w32_mask(0, 0x2, RTL838X_IO_DRIVING_ABILITY_CTRL);
|
|
|
|
/* Enter simulated GPIO mode */
|
|
sw_w32_mask(1, 0, RTL838X_EXTRA_GPIO_CTRL);
|
|
|
|
/* MDIO clock to 2.6MHz */
|
|
sw_w32_mask(0x3 << 8, 0, RTL838X_EXTRA_GPIO_CTRL);
|
|
|
|
/* Configure SMI clock and data GPIO pins */
|
|
rtl838x_w32_mask((1 << mdc) | (1 << mdio), 0, RTL838X_GPIO_PABC_CNR);
|
|
rtl838x_w32_mask(0, (1 << mdc) | (1 << mdio), RTL838X_GPIO_PABC_DIR);
|
|
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL0, 0xffff);
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL1, 0xffff);
|
|
rtl8380_smi_read(gpios, RTL8231_GPIO_PIN_SEL2, &v);
|
|
v |= 0x1f;
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL2, v);
|
|
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_IO_SEL0, 0xffff);
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_IO_SEL1, 0xffff);
|
|
rtl8380_smi_read(gpios, RTL8231_GPIO_IO_SEL2, &v);
|
|
v |= 0x1f << 5;
|
|
rtl8380_smi_write(gpios, RTL8231_GPIO_PIN_SEL2, v);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rtl8380_led_test(u32 mask)
|
|
{
|
|
int i;
|
|
u32 mode_sel = sw_r32(RTL838X_LED_MODE_SEL);
|
|
u32 led_gbl = sw_r32(RTL838X_LED_GLB_CTRL);
|
|
u32 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), RTL838X_LED_GLB_CTRL);
|
|
/* Enable all leds */
|
|
sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL);
|
|
|
|
/* Enable software control of all leds */
|
|
sw_w32(0xFFFFFFF, RTL838X_LED_SW_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED0_SW_P_EN_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED1_SW_P_EN_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL);
|
|
|
|
for (i = 0; i < 28; i++) {
|
|
if (mask & (1 << i))
|
|
sw_w32(5 | (5 << 3) | (5 << 6),
|
|
RTL838X_LED_SW_P_CTRL(i));
|
|
}
|
|
msleep(3000);
|
|
|
|
sw_w32(led_p_en, RTL838X_LED_P_EN_CTRL);
|
|
/* Disable software control of all leds */
|
|
sw_w32(0x0000000, RTL838X_LED_SW_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED0_SW_P_EN_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED1_SW_P_EN_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL);
|
|
|
|
sw_w32(led_gbl, RTL838X_LED_GLB_CTRL);
|
|
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(RTL838X_LED0_SW_P_EN_CTRL),
|
|
sw_r32(RTL838X_LED1_SW_P_EN_CTRL),
|
|
sw_r32(RTL838X_LED2_SW_P_EN_CTRL)
|
|
);
|
|
|
|
pr_debug("led glb: %x, sel %x\n",
|
|
sw_r32(RTL838X_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, RTL838X_LED_SW_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED_P_EN_CTRL);
|
|
|
|
sw_w32(0x0000000, RTL838X_LED0_SW_P_EN_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED1_SW_P_EN_CTRL);
|
|
sw_w32(0x0000000, RTL838X_LED2_SW_P_EN_CTRL);
|
|
|
|
sw_w32_mask(0x3f, 0, RTL838X_LED_GLB_CTRL);
|
|
switch (leds_per_port) {
|
|
case 3:
|
|
sw_w32_mask(0, 0x7 | (0x7 << 3), RTL838X_LED_GLB_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED2_SW_P_EN_CTRL);
|
|
/* FALLTHRU */
|
|
case 2:
|
|
sw_w32_mask(0, 0x3 | (0x3 << 3), RTL838X_LED_GLB_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED1_SW_P_EN_CTRL);
|
|
/* FALLTHRU */
|
|
case 1:
|
|
sw_w32_mask(0, 0x1 | (0x1 << 3), RTL838X_LED_GLB_CTRL);
|
|
sw_w32(0xFFFFFFF, RTL838X_LED0_SW_P_EN_CTRL);
|
|
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;
|
|
u8 indirect_bus_id;
|
|
|
|
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 = sw_r32(RTL838X_MODEL_NAME_INFO) >> 16;
|
|
|
|
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;
|
|
default:
|
|
pr_err("Unknown GPIO chip id (%04x)\n", gpios->id);
|
|
return -ENODEV;
|
|
}
|
|
|
|
gpios->dev = dev;
|
|
gpios->gc.base = 0;
|
|
/* 0-31: internal
|
|
* 32-63, LED control register
|
|
* 64-99: external RTL8231
|
|
* 100-131: PORT-LED 0
|
|
* 132-163: PORT-LED 1
|
|
* 164-195: PORT-LED 2
|
|
*/
|
|
gpios->gc.ngpio = 196;
|
|
gpios->gc.label = "rtl838x";
|
|
gpios->gc.parent = dev;
|
|
gpios->gc.owner = THIS_MODULE;
|
|
gpios->gc.can_sleep = true;
|
|
gpios->bus_id = -1;
|
|
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_u8(np, "indirect-access-bus-id", &indirect_bus_id)) {
|
|
gpios->bus_id = indirect_bus_id;
|
|
rtl8231_init(gpios);
|
|
}
|
|
if (!of_property_read_u8(np, "smi-bus-id", &indirect_bus_id)) {
|
|
gpios->bus_id = indirect_bus_id;
|
|
gpios->smi_clock = RTL838X_GPIO_A2;
|
|
gpios->smi_data = RTL838X_GPIO_A3;
|
|
gpios->i2c_sda = 1;
|
|
gpios->i2c_sdc = 2;
|
|
rtl8380_rtl8321_init(gpios);
|
|
}
|
|
|
|
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");
|