blob: 8d9c5ff30ff224a270741a4800b2e9ce54e7cc44 [file] [log] [blame]
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*qca808x_start*/
#include "sw.h"
#include "ssdk_phy_i2c.h"
#include <linux/i2c.h>
#include "ssdk_init.h"
#include "ssdk_plat.h"
#define I2C_RW_LIMIT 8
#define I2C_ADAPTER_DEFAULT_ID 0
/******************************************************************************
*
* _qca_i2c_read - read data per i2c bus
*
* read data per i2c bus
*/
static inline a_int16_t
_qca_i2c_read(a_uint32_t i2c_bus_id, a_uint32_t i2c_slave,
a_uint32_t data_addr, a_uint8_t *buf, a_uint32_t count)
{
a_int16_t ret, i;
struct i2c_adapter *adapt;
a_uint8_t addrbuf[2];
struct i2c_msg msg[2];
ret =0;
i = 0;
if (data_addr & 0xff00) {
addrbuf[i++] = (data_addr >> 8) & 0xff;
}
addrbuf[i++] = data_addr & 0xff;
if (count > I2C_RW_LIMIT) {
count = I2C_RW_LIMIT;
}
/*
* msg for configuring the address
*/
aos_mem_set(msg, 0, sizeof(msg));
msg[0].addr = i2c_slave;
msg[0].len = i;
msg[0].buf = addrbuf;
/*
* msg for acquiring data
*/
msg[1].addr = i2c_slave;
msg[1].flags = I2C_M_RD;
msg[1].len = count;
msg[1].buf = buf;
adapt = i2c_get_adapter(i2c_bus_id);
if (adapt) {
ret = i2c_transfer(adapt, msg, ARRAY_SIZE(msg));
i2c_put_adapter(adapt);
}
if (ret == ARRAY_SIZE(msg)) {
return count;
}
return ret;
}
/******************************************************************************
*
* qca_i2c_data_get - wrapper of reading data per i2c bus
*
* wrapper of reading data per i2c bus
*/
sw_error_t
qca_i2c_data_get(a_uint32_t dev_id, a_uint32_t i2c_slave,
a_uint32_t data_addr, a_uint8_t *buf, a_uint32_t count)
{
a_int16_t ret = 0, cur = 0;
a_uint16_t cnt = count;
while (cnt) {
cur = _qca_i2c_read(I2C_ADAPTER_DEFAULT_ID,
i2c_slave, data_addr, buf, cnt);
/* No such i2c_slave device */
if (cur == -ENXIO) {
return SW_NO_RESOURCE;
}
if (cur <= 0) {
break;
}
/*
* loop to acquire the data from the new
* address based on the returned count.
*/
cnt -= cur;
buf += cur;
data_addr += cur;
ret += cur;
}
if (ret != count) {
return SW_FAIL;
}
return SW_OK;
}
/******************************************************************************
*
* _qca_i2c_write - write data per i2c bus
*
* write data per i2c bus
*/
static inline a_int16_t
_qca_i2c_write(a_uint32_t i2c_bus_id, a_uint32_t i2c_slave,
a_uint32_t data_addr, a_uint8_t *buf, a_uint32_t count)
{
a_int16_t ret, i;
struct i2c_adapter *adapt;
struct i2c_msg msg;
a_uint8_t i2c_wbuf[I2C_RW_LIMIT+2];
ret = 0;
i = 0;
if (data_addr & 0xff00) {
i2c_wbuf[i++] = (data_addr >> 8) & 0xff;
}
/*
* the write buffer is for saving address
* and the data to sent.
*/
i2c_wbuf[i++] = data_addr & 0xff;
if (count > I2C_RW_LIMIT) {
count = I2C_RW_LIMIT;
}
memcpy(&i2c_wbuf[i], buf, count);
aos_mem_set(&msg, 0, sizeof(msg));
msg.addr = i2c_slave;
msg.len = i + count;
msg.buf = i2c_wbuf;
adapt = i2c_get_adapter(i2c_bus_id);
if (adapt) {
ret = i2c_transfer(adapt, &msg, 1);
i2c_put_adapter(adapt);
}
if (ret == 1) {
return count;
}
return ret;
}
/******************************************************************************
*
* qca_i2c_data_set - wrapper of writting data per i2c bus
*
* wrapper of writting data per i2c bus
*/
sw_error_t
qca_i2c_data_set(a_uint32_t dev_id, a_uint32_t i2c_slave,
a_uint32_t data_addr, a_uint8_t *buf, a_uint32_t count)
{
a_int16_t ret = 0, cur = 0;
a_uint16_t cnt = count;
while (cnt) {
cur = _qca_i2c_write(I2C_ADAPTER_DEFAULT_ID,
i2c_slave, data_addr, buf, cnt);
/* No such i2c_slave device */
if (cur == -ENXIO) {
return SW_NO_RESOURCE;
}
if (cur <= 0) {
break;
}
/*
* loop to write the data to the new
* address based on the returned count.
*/
cnt -= cur;
buf += cur;
data_addr += cur;
ret += cur;
}
if (ret != count) {
return SW_FAIL;
}
return SW_OK;
}
/******************************************************************************
*
* _qca_phy_i2c_mii_read - mii register i2c read
*
* mii register i2c read
*/
sw_error_t
qca_phy_i2c_mii_read(a_uint32_t dev_id, a_uint32_t phy_addr,
a_uint32_t reg_addr, a_uint16_t *reg_data)
{
sw_error_t ret;
a_uint8_t rx[2] = { 0 };
reg_addr &= 0xff;
ret = qca_i2c_data_get(dev_id, phy_addr,
reg_addr, rx, sizeof(rx));
if (ret == SW_OK) {
*reg_data = (rx[0] << 8) | rx[1];
} else {
*reg_data = 0xffff;
}
return ret;
}
/******************************************************************************
*
* _qca_phy_i2c_mii_write - mii register i2c write
*
* mii register i2c write
*/
sw_error_t
qca_phy_i2c_mii_write(a_uint32_t dev_id, a_uint32_t phy_addr,
a_uint32_t reg_addr, a_uint16_t reg_data)
{
sw_error_t ret;
a_uint8_t tx[2] = {(reg_data >> 8) & 0xff, reg_data & 0xff};
reg_addr &= 0xff;
ret = qca_i2c_data_set(dev_id, phy_addr,
reg_addr, tx, sizeof(tx));
return ret;
}
#ifdef IN_PHY_I2C_MODE
/******************************************************************************
*
* _qca_phy_i2c_mmd_read - mmd register i2c read
*
* mmd register i2c read
*/
sw_error_t
qca_phy_i2c_mmd_read(a_uint32_t dev_id, a_uint32_t phy_addr, a_uint16_t mmd_num,
a_uint32_t reg_addr, a_uint16_t *reg_data)
{
a_uint8_t ret;
struct i2c_adapter *adapt;
/*
TX buffer to send:
1st byte: Bit[6](1:MMD 0:MII) Bit[5](1:MMD address 0:MMD data) Bit[4:0](MMD num)
2nd byte is high 8bits of reg addr
3rd byte is low 8bits of reg addr
4th byte: add one extra byte at the end of address writing msg to avoid qca808x
treat it as data writing operation
*/
a_uint8_t tx[4] = { ((QCA_PHY_I2C_IS_MMD << QCA_PHY_I2C_MMD_OR_MII_SHIFT) |
(QCA_PHY_I2C_MMD_IS_ADDR << QCA_PHY_I2C_MMD_ADDR_OR_DATA_SHIFT) | mmd_num),
(reg_addr >> 8) & 0xff, reg_addr & 0xff, 0 };
/*
RX buffer to receive:
1st byte is high 8bits of reg data
2nd byte is low 8bits of reg data
*/
a_uint8_t rx[2] = { 0 };
struct i2c_msg msg[] = {
{ .addr = phy_addr, .flags = 0,
.buf = tx, .len = sizeof(tx) },
{ .addr = phy_addr, .flags = I2C_M_RD,
.buf = rx, .len = sizeof(rx) } };
if((mmd_num != QCA_PHY_MMD1_NUM) && (mmd_num != QCA_PHY_MMD3_NUM) &&
(mmd_num != QCA_PHY_MMD7_NUM)) {
SSDK_ERROR("wrong MMD number:[%d]\n", mmd_num);
return SW_FAIL;
}
adapt = i2c_get_adapter(I2C_ADAPTER_DEFAULT_ID);
if (!adapt) {
return SW_FAIL;
}
ret = i2c_transfer(adapt, msg, ARRAY_SIZE(msg));
*reg_data = (rx[0] << 8) | rx[1];
i2c_put_adapter(adapt);
if (ret != ARRAY_SIZE(msg)) {
return SW_FAIL;
}
return SW_OK;
}
/******************************************************************************
*
* _qca_phy_i2c_mmd_write - mmd register i2c write
*
* mmd register i2c write
*/
sw_error_t
qca_phy_i2c_mmd_write(a_uint32_t dev_id, a_uint32_t phy_addr, a_uint16_t mmd_num,
a_uint32_t reg_addr, a_uint16_t reg_data)
{
a_uint8_t ret;
struct i2c_adapter *adapt;
/*
First TX buffer to send:
1st byte: Bit[6](1:MMD 0:MII) Bit[5](1:MMD address 0:MMD data) Bit[4:0](MMD num)
2nd byte is high 8bits of reg addr
3rd byte is low 8bits of reg addr
4th byte: add one extra byte at the end of address writing msg to avoid qca808x
treat it as data writing operation
*/
a_uint8_t tx[4] = { ((QCA_PHY_I2C_IS_MMD << QCA_PHY_I2C_MMD_OR_MII_SHIFT) |
(QCA_PHY_I2C_MMD_IS_ADDR << QCA_PHY_I2C_MMD_ADDR_OR_DATA_SHIFT) | mmd_num),
(reg_addr >> 8) & 0xff, reg_addr & 0xff, 0 };
/*
Second TX buffer to send:
1st byte: Bit[6](1:MMD 0:MII) Bit[5](1:MMD address 0:MMD data) Bit[4:0](MMD num)
2nd byte is high 8bits of reg data
3rd byte is low 8bits of reg data
*/
a_uint8_t tx2[3] = { ((QCA_PHY_I2C_IS_MMD << QCA_PHY_I2C_MMD_OR_MII_SHIFT) |
(QCA_PHY_I2C_MMD_IS_DATA << QCA_PHY_I2C_MMD_ADDR_OR_DATA_SHIFT) | mmd_num),
(reg_data >> 8) & 0xff, reg_data & 0xff };
struct i2c_msg msg[] = {
{ .addr = phy_addr, .flags = 0,
.buf = tx, .len = sizeof(tx) },
{ .addr = phy_addr, .flags = 0,
.buf = tx2, .len = sizeof(tx2) } };
if((mmd_num != QCA_PHY_MMD1_NUM) && (mmd_num != QCA_PHY_MMD3_NUM) &&
(mmd_num != QCA_PHY_MMD7_NUM)) {
SSDK_ERROR("wrong MMD number:[%d]\n", mmd_num);
return SW_FAIL;
}
adapt = i2c_get_adapter(I2C_ADAPTER_DEFAULT_ID);
if (!adapt) {
return SW_FAIL;
}
ret = i2c_transfer(adapt, msg, ARRAY_SIZE(msg));
i2c_put_adapter(adapt);
if (ret != ARRAY_SIZE(msg)) {
return SW_FAIL;
}
return SW_OK;
}
/******************************************************************************
*
* qca_phy_is_i2c_addr - check if phy addr is i2c addr
*
* check if phy addr is i2c addr
*/
a_bool_t
qca_phy_is_i2c_addr(a_uint32_t phy_addr)
{
if(((phy_addr & QCA_PHY_I2C_DEVADDR_MASK) ==
QCA_PHY_I2C_PHYCORE_DEVADDR) ||
((phy_addr & QCA_PHY_I2C_DEVADDR_MASK) ==
QCA_PHY_I2C_SERDES_DEVADDR)) {
return A_TRUE;
} else {
return A_FALSE;
}
}
/******************************************************************************
*
* qca_phy_i2c_read - PHY register i2c read
*
* PHY register i2c read
*/
sw_error_t
qca_phy_i2c_read(a_uint32_t dev_id, a_uint32_t phy_addr,
a_uint32_t reg_addr_c45, a_uint16_t *reg_data)
{
a_uint32_t mmd_num = QCA_PHY_MII_ADDR_C45_MMD_NUM(reg_addr_c45);
a_uint32_t reg_addr = QCA_PHY_MII_ADDR_C45_REG_ADDR(reg_addr_c45);
if(qca_phy_is_i2c_addr(phy_addr) == A_FALSE) {
return SW_BAD_PARAM;
}
if(QCA_PHY_MII_ADDR_C45_IS_MMD(reg_addr_c45)) {
qca_phy_i2c_mmd_read(dev_id, phy_addr, mmd_num, reg_addr, reg_data);
} else {
qca_phy_i2c_mii_read(dev_id, phy_addr, reg_addr, reg_data);
}
return SW_OK;
}
/******************************************************************************
*
* qca_phy_i2c_write - PHY register i2c write
*
* PHY register i2c write
*/
sw_error_t
qca_phy_i2c_write(a_uint32_t dev_id, a_uint32_t phy_addr,
a_uint32_t reg_addr_c45, a_uint16_t reg_data)
{
a_uint32_t mmd_num = QCA_PHY_MII_ADDR_C45_MMD_NUM(reg_addr_c45);
a_uint32_t reg_addr = QCA_PHY_MII_ADDR_C45_REG_ADDR(reg_addr_c45);
if(qca_phy_is_i2c_addr(phy_addr) == A_FALSE) {
return SW_BAD_PARAM;
}
if(QCA_PHY_MII_ADDR_C45_IS_MMD(reg_addr_c45)) {
qca_phy_i2c_mmd_write(dev_id, phy_addr, mmd_num, reg_addr, reg_data);
} else {
qca_phy_i2c_mii_write(dev_id, phy_addr, reg_addr, reg_data);
}
return SW_OK;
}
/*qca808x_end*/
EXPORT_SYMBOL(qca_phy_i2c_read);
EXPORT_SYMBOL(qca_phy_i2c_write);
/*qca808x_start*/
#endif
/*qca808x_start*/