Files
android/drivers/misc/mediatek/mu3phy/mtk-phy-gpio.c
T
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

505 lines
12 KiB
C

/*
* Copyright (C) 2015 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mtk-phy.h"
#ifdef CONFIG_U3_PHY_GPIO_SUPPORT
/* TEST CHIP PHY define, edit this in different platform */
#define U3_PHY_I2C_DEV 0x60
#define U3_PHY_PAGE 0xff
#define GPIO_BASE (u3_ippc_base)
#define SSUSB_I2C_OUT (GPIO_BASE + 0xd0)
#define SSUSB_I2C_IN (GPIO_BASE + 0xd4)
#ifdef NEVER /*USE_GPIO */
/* /////////////////////////////////////////////////////////////// */
#define OUTPUT 1
#define INPUT 0
#define SDA 0 /* / GPIO #0: I2C data pin */
#define SCL 1 /* / GPIO #1: I2C clock pin */
/* /////////////////////////////////////////////////////////////// */
#define SDA_OUT (1<<0)
#define SDA_OEN (1<<1)
#define SCL_OUT (1<<2)
#define SCL_OEN (1<<3)
#define SDA_IN_OFFSET 0
#define SCL_IN_OFFSET 1
/* #define GPIO_PULLEN1_SET (GPIO_BASE+0x0030+0x04) */
/* #define GPIO_DIR1_SET (GPIO_BASE+0x0000+0x04) */
/* #define GPIO_PULLEN1_CLR (GPIO_BASE+0x0030+0x08) */
/* #define GPIO_DIR1_CLR (GPIO_BASE+0x0000+0x08) */
/* #define GPIO_DOUT1_SET (GPIO_BASE+0x00C0+0x04) */
/* #define GPIO_DOUT1_CLR (GPIO_BASE+0x00C0+0x08) */
/* #define GPIO_DIN1 (GPIO_BASE+0x00F0) */
void gpio_dir_set(PHY_INT32 pin)
{
PHY_INT32 temp;
u64 addr;
addr = (u64)SSUSB_I2C_OUT;
temp = DRV_Reg32(addr);
if (pin == SDA) {
temp |= SDA_OEN;
DRV_WriteReg32(addr, temp);
} else {
temp |= SCL_OEN;
DRV_WriteReg32(addr, temp);
}
}
void gpio_dir_clr(PHY_INT32 pin)
{
PHY_INT32 temp;
u64 addr;
addr = (u64)SSUSB_I2C_OUT;
temp = DRV_Reg32(addr);
if (pin == SDA) {
temp &= ~SDA_OEN;
DRV_WriteReg32(addr, temp);
} else {
temp &= ~SCL_OEN;
DRV_WriteReg32(addr, temp);
}
}
void gpio_dout_set(PHY_INT32 pin)
{
PHY_INT32 temp;
u64 addr;
addr = (u64)SSUSB_I2C_OUT;
temp = DRV_Reg32(addr);
if (pin == SDA) {
temp |= SDA_OUT;
DRV_WriteReg32(addr, temp);
} else {
temp |= SCL_OUT;
DRV_WriteReg32(addr, temp);
}
}
void gpio_dout_clr(PHY_INT32 pin)
{
PHY_INT32 temp;
u64 addr;
addr = (u64)SSUSB_I2C_OUT;
temp = DRV_Reg32(addr);
if (pin == SDA) {
temp &= ~SDA_OUT;
DRV_WriteReg32(addr, temp);
} else {
temp &= ~SCL_OUT;
DRV_WriteReg32(addr, temp);
}
}
PHY_INT32 gpio_din(PHY_INT32 pin)
{
PHY_INT32 temp;
u64 addr;
addr = (u64)SSUSB_I2C_IN;
temp = DRV_Reg32(addr);
if (pin == SDA)
temp = (temp >> SDA_IN_OFFSET) & 1;
else
temp = (temp >> SCL_IN_OFFSET) & 1;
return temp;
}
/* #define GPIO_PULLEN_SET(_no) (GPIO_PULLEN1_SET+(0x10*(_no))) */
#define GPIO_DIR_SET(pin) gpio_dir_set(pin)
#define GPIO_DOUT_SET(pin) gpio_dout_set(pin)
/* #define GPIO_PULLEN_CLR(_no) (GPIO_PULLEN1_CLR+(0x10*(_no))) */
#define GPIO_DIR_CLR(pin) gpio_dir_clr(pin)
#define GPIO_DOUT_CLR(pin) gpio_dout_clr(pin)
#define GPIO_DIN(pin) gpio_din(pin)
PHY_UINT32 i2c_dummy_cnt;
#define I2C_DELAY 10
#define I2C_DUMMY_DELAY(_delay) for (i2c_dummy_cnt = ((_delay)); i2c_dummy_cnt != 0; i2c_dummy_cnt--)
void GPIO_InitIO(PHY_UINT32 dir, PHY_UINT32 pin)
{
if (dir == OUTPUT) {
/* DRV_WriteReg16(GPIO_PULLEN_SET(no),(1 << remainder)); */
GPIO_DIR_SET(pin);
} else {
/* DRV_WriteReg16(GPIO_PULLEN_CLR(no),(1 << remainder)); */
GPIO_DIR_CLR(pin);
}
I2C_DUMMY_DELAY(100);
}
void GPIO_WriteIO(PHY_UINT32 data, PHY_UINT32 pin)
{
if (data == 1)
GPIO_DOUT_SET(pin);
else
GPIO_DOUT_CLR(pin);
}
PHY_UINT32 GPIO_ReadIO(PHY_UINT32 pin)
{
PHY_UINT16 data;
data = GPIO_DIN(pin);
return (PHY_UINT32) data;
}
void SerialCommStop(void)
{
GPIO_InitIO(OUTPUT, SDA);
GPIO_WriteIO(0, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_InitIO(INPUT, SCL);
GPIO_InitIO(INPUT, SDA);
}
void SerialCommStart(void)
{ /* Prepare the SDA and SCL for sending/receiving */
GPIO_InitIO(OUTPUT, SCL);
GPIO_InitIO(OUTPUT, SDA);
GPIO_WriteIO(1, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
}
PHY_UINT32 SerialCommTxByte(PHY_UINT8 data)
{ /* return 0 --> ack */
PHY_INT32 i, ack;
GPIO_InitIO(OUTPUT, SDA);
for (i = 8; --i > 0;) {
GPIO_WriteIO((data >> i) & 0x01, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL); /* high */
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SCL); /* low */
I2C_DUMMY_DELAY(I2C_DELAY);
}
GPIO_WriteIO((data >> i) & 0x01, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL); /* high */
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SCL); /* low */
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_InitIO(INPUT, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
ack = GPIO_ReadIO(SDA); /* / ack 1: error , 0:ok */
GPIO_WriteIO(0, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
if (ack == 1)
return PHY_FALSE;
else
return PHY_TRUE;
}
void SerialCommRxByte(PHY_UINT8 *data, PHY_UINT8 ack)
{
PHY_INT32 i;
PHY_UINT32 dataCache;
dataCache = 0;
GPIO_InitIO(INPUT, SDA);
for (i = 8; --i >= 0;) {
dataCache <<= 1;
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
dataCache |= GPIO_ReadIO(SDA);
GPIO_WriteIO(0, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
}
GPIO_InitIO(OUTPUT, SDA);
GPIO_WriteIO(ack, SDA);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(1, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
GPIO_WriteIO(0, SCL);
I2C_DUMMY_DELAY(I2C_DELAY);
*data = (unsigned char)dataCache;
}
PHY_INT32 I2cWriteReg(PHY_UINT8 dev_id, PHY_UINT8 Addr, PHY_UINT8 Data)
{
PHY_INT32 acknowledge = 0;
SerialCommStart();
acknowledge = SerialCommTxByte((dev_id << 1) & 0xff);
if (acknowledge)
acknowledge = SerialCommTxByte(Addr);
else
return PHY_FALSE;
acknowledge = SerialCommTxByte(Data);
if (acknowledge) {
SerialCommStop();
return PHY_FALSE;
} else {
return PHY_TRUE;
}
}
PHY_INT32 I2cReadReg(PHY_UINT8 dev_id, PHY_UINT8 Addr, PHY_UINT8 *Data)
{
PHY_INT32 acknowledge = 0;
SerialCommStart();
acknowledge = SerialCommTxByte((dev_id << 1) & 0xff);
if (acknowledge)
acknowledge = SerialCommTxByte(Addr);
else
return PHY_FALSE;
SerialCommStart();
acknowledge = SerialCommTxByte(((dev_id << 1) & 0xff) | 0x01);
if (acknowledge)
SerialCommRxByte(Data, 1); /* ack 0: ok , 1 error */
else
return PHY_FALSE;
SerialCommStop();
return acknowledge;
}
#else /* Use I2C controller */
#define REG_I2C_START_BIT 0x1
#define I2C_READ_BIT 0x1
#define PHY_I2C_BASE (i2c_base)
/* "volatile" type class should not be used, see volatile-considered-harmful.txt */
#define REG_I2C_DATA_PORT (*((volatile unsigned short int *) (PHY_I2C_BASE + 0x00)))
#define REG_I2C_SLAVE_ADDR (*((volatile unsigned short int *) (PHY_I2C_BASE + 0x04)))
#define REG_I2C_TRANSFER_LEN (*((volatile unsigned short int *) (PHY_I2C_BASE + 0x14)))
#define REG_I2C_START (*((volatile unsigned short int *) (PHY_I2C_BASE + 0x24)))
#define REG_I2C_SOFT_RESET (*((volatile unsigned short int *) (PHY_I2C_BASE + 0x50)))
#define REG_I2C_CONTROL (*((unsigned short int *) (PHY_I2C_BASE + 0x10)))
/* ADDITIONAL CONTROL */
#define REG_I2C_TRANSAC_LEN (PHY_I2C_BASE + 0x18)
#define REG_I2C_HTIMING (PHY_I2C_BASE + 0x20)
#define REG_I2C_LTIMING (PHY_I2C_BASE + 0x2C)
#define ADDR_I2C_DATA_PORT (PHY_I2C_BASE + 0x00)
#define ADDR_I2C_SLAVE_ADDR (PHY_I2C_BASE + 0x04)
#define ADDR_I2C_TRANSFER_LEN (PHY_I2C_BASE + 0x14)
#define ADDR_I2C_START (PHY_I2C_BASE + 0x24)
#define IS_PRINT 1
PHY_INT32 I2cWriteReg(PHY_UINT8 dev_id, PHY_UINT8 addr, PHY_UINT8 val)
{
if (IS_PRINT)
pr_info("I2C Write@%x [%x]=%x\n", dev_id, addr, val);
writew((dev_id << 1), ADDR_I2C_SLAVE_ADDR);
writew(2, ADDR_I2C_TRANSFER_LEN);
/* ADDITIONAL CONTROL */
writew(0x1, REG_I2C_TRANSAC_LEN);
writew(0x1303, REG_I2C_HTIMING);
writew(0x13C3, REG_I2C_LTIMING);
writew(addr, ADDR_I2C_DATA_PORT);
writew(val, ADDR_I2C_DATA_PORT);
writew(REG_I2C_START_BIT, ADDR_I2C_START);
while ((readw(ADDR_I2C_START) & REG_I2C_START_BIT))
;
return PHY_TRUE;
}
PHY_INT32 I2cReadReg(PHY_UINT8 dev_id, PHY_UINT8 addr, PHY_UINT8 *data)
{
if (IS_PRINT)
pr_info("I2C Read@%x [%x]\n", dev_id, addr);
writew((dev_id << 1), ADDR_I2C_SLAVE_ADDR);
writew(1, ADDR_I2C_TRANSFER_LEN);
/* ADDITIONAL CONTROL */
writew(0x1, REG_I2C_TRANSAC_LEN);
writew(0x1303, REG_I2C_HTIMING);
writew(0x13C3, REG_I2C_LTIMING);
writew(addr, ADDR_I2C_DATA_PORT);
writew(REG_I2C_START_BIT, ADDR_I2C_START);
while ((readw(ADDR_I2C_START) & REG_I2C_START_BIT))
;
writew((dev_id << 1) | I2C_READ_BIT, ADDR_I2C_SLAVE_ADDR);
writew(1, ADDR_I2C_TRANSFER_LEN);
/* ADDITIONAL CONTROL */
writew(0x1, REG_I2C_TRANSAC_LEN);
writew(0x1303, REG_I2C_HTIMING);
writew(0x13C3, REG_I2C_LTIMING);
writew(REG_I2C_START_BIT, ADDR_I2C_START);
while ((readw(ADDR_I2C_START) & REG_I2C_START_BIT))
;
*data = readw(ADDR_I2C_DATA_PORT);
if (IS_PRINT)
pr_info("I2C Read [%x]=%x\n", addr, *data);
return PHY_TRUE; /* !!(PHY_INT32)*data; */
}
#endif
void _U3_Write_Bank(PHY_INT32 bankValue)
{
I2cWriteReg(U3_PHY_I2C_DEV, U3_PHY_PAGE, bankValue);
}
PHY_INT32 _U3Write_Reg(PHY_INT32 address, PHY_INT32 value)
{
I2cWriteReg(U3_PHY_I2C_DEV, address, value);
return PHY_TRUE;
}
PHY_INT32 _U3Read_Reg(PHY_INT32 address)
{
PHY_INT8 *pu1Buf;
PHY_INT32 ret;
pu1Buf = kmalloc(1, GFP_NOIO);
if (!pu1Buf) {
ret = -ENOMEM;
pr_err("%s - NOMEM\n", __func__);
return ret;
}
ret = I2cReadReg(U3_PHY_I2C_DEV, address, pu1Buf);
if (ret == PHY_FALSE) {
pr_err("Read failed\n");
return PHY_FALSE;
}
ret = (char)pu1Buf[0];
kfree(pu1Buf);
return ret;
}
PHY_INT32 U3PhyWriteReg32(PHY_UINT32 addr, PHY_UINT32 data)
{
PHY_INT32 bank;
PHY_INT32 addr8;
PHY_INT32 data_0, data_1, data_2, data_3;
bank = (addr >> 16) & 0xff;
addr8 = addr & 0xff;
data_0 = data & 0xff;
data_1 = (data >> 8) & 0xff;
data_2 = (data >> 16) & 0xff;
data_3 = (data >> 24) & 0xff;
pr_debug("addr: %x, data: %x\n", addr8, data);
_U3_Write_Bank(bank);
pr_debug("addr: %x, data: %x\n", addr8, data_0);
_U3Write_Reg(addr8, data_0);
pr_debug("addr: %x, data: %x\n", addr8 + 1, data_1);
_U3Write_Reg(addr8 + 1, data_1);
pr_debug("addr: %x, data: %x\n", addr8 + 2, data_2);
_U3Write_Reg(addr8 + 2, data_2);
pr_debug("addr: %x, data: %x\n", addr8 + 3, data_3);
_U3Write_Reg(addr8 + 3, data_3);
return 0;
}
PHY_INT32 U3PhyReadReg32(PHY_UINT32 addr)
{
PHY_INT32 bank;
PHY_INT32 addr8;
PHY_INT32 data;
bank = (addr >> 16) & 0xff;
addr8 = addr & 0xff;
_U3_Write_Bank(bank);
data = _U3Read_Reg(addr8);
data |= (_U3Read_Reg(addr8 + 1) << 8);
data |= (_U3Read_Reg(addr8 + 2) << 16);
data |= (_U3Read_Reg(addr8 + 3) << 24);
return data;
}
PHY_INT32 U3PhyWriteReg8(PHY_UINT32 addr, PHY_UINT8 data)
{
PHY_INT32 bank;
PHY_INT32 addr8;
bank = (addr >> 16) & 0xff;
addr8 = addr & 0xff;
_U3_Write_Bank(bank);
_U3Write_Reg(addr8, data);
pr_debug("addr: %x, data: %x\n", addr8, data);
return PHY_TRUE;
}
PHY_INT8 U3PhyReadReg8(PHY_UINT32 addr)
{
PHY_INT32 bank;
PHY_INT32 addr8;
PHY_INT32 data;
bank = (addr >> 16) & 0xff;
addr8 = addr & 0xff;
_U3_Write_Bank(bank);
data = _U3Read_Reg(addr8);
return data;
}
#endif