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)
505 lines
12 KiB
C
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
|