888 lines
28 KiB
C
888 lines
28 KiB
C
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/delay.h>
|
|
#include "io_expander.h"
|
|
|
|
static struct ioexp_obj_s *ioexp_head_p = NULL;
|
|
static struct ioexp_obj_s *ioexp_tail_p = NULL;
|
|
|
|
struct ioexp_map_s ioexp_map_redwood_p01p08_p17p24 = {
|
|
|
|
.chip_amount = 3,
|
|
.data_width = 2,
|
|
|
|
.map_present = { {2, 0, 0}, /* map_present[0] = MOD_ABS_PORT(X) */
|
|
{2, 0, 1}, /* map_present[1] = MOD_ABS_PORT(X+1) */
|
|
{2, 0, 2}, /* map_present[2] = MOD_ABS_PORT(X+2) */
|
|
{2, 0, 3}, /* map_present[3] = MOD_ABS_PORT(X+3) */
|
|
{2, 0, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
|
|
{2, 0, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
|
|
{2, 0, 6}, /* map_present[6] = MOD_ABS_PORT(X+6) */
|
|
{2, 0, 7}, /* map_present[7] = MOD_ABS_PORT(X+7) */
|
|
},
|
|
.map_reset = { {0, 0, 0}, /* map_reset[0] = QRESET_QSFP28_N_P(X) */
|
|
{0, 0, 1}, /* map_reset[1] = QRESET_QSFP28_N_P(X+1) */
|
|
{0, 0, 2}, /* map_reset[2] = QRESET_QSFP28_N_P(X+2) */
|
|
{0, 0, 3}, /* map_reset[3] = QRESET_QSFP28_N_P(X+3) */
|
|
{1, 0, 0}, /* map_reset[4] = QRESET_QSFP28_N_P(X+4) */
|
|
{1, 0, 1}, /* map_reset[5] = QRESET_QSFP28_N_P(X+5) */
|
|
{1, 0, 2}, /* map_reset[6] = QRESET_QSFP28_N_P(X+6) */
|
|
{1, 0, 3}, /* map_reset[7] = QRESET_QSFP28_N_P(X+7) */
|
|
},
|
|
.map_lpmod = { {0, 0, 4}, /* map_lpmod[0] = LPMODE_QSFP28_P(X) */
|
|
{0, 0, 5}, /* map_lpmod[1] = LPMODE_QSFP28_P(X+1) */
|
|
{0, 0, 6}, /* map_lpmod[2] = LPMODE_QSFP28_P(X+2) */
|
|
{0, 0, 7}, /* map_lpmod[3] = LPMODE_QSFP28_P(X+3) */
|
|
{1, 0, 4}, /* map_lpmod[4] = LPMODE_QSFP28_P(X+4) */
|
|
{1, 0, 5}, /* map_lpmod[5] = LPMODE_QSFP28_P(X+5) */
|
|
{1, 0, 6}, /* map_lpmod[6] = LPMODE_QSFP28_P(X+6) */
|
|
{1, 0, 7}, /* map_lpmod[7] = LPMODE_QSFP28_P(X+7) */
|
|
},
|
|
.map_modsel = { {0, 1, 4}, /* map_modsel[0] = MODSEL_QSFP28_N_P(X) */
|
|
{0, 1, 5}, /* map_modsel[1] = MODSEL_QSFP28_N_P(X+1) */
|
|
{0, 1, 6}, /* map_modsel[2] = MODSEL_QSFP28_N_P(X+2) */
|
|
{0, 1, 7}, /* map_modsel[3] = MODSEL_QSFP28_N_P(X+3) */
|
|
{1, 1, 4}, /* map_modsel[4] = MODSEL_QSFP28_N_P(X+4) */
|
|
{1, 1, 5}, /* map_modsel[5] = MODSEL_QSFP28_N_P(X+5) */
|
|
{1, 1, 6}, /* map_modsel[6] = MODSEL_QSFP28_N_P(X+6) */
|
|
{1, 1, 7}, /* map_modsel[7] = MODSEL_QSFP28_N_P(X+7) */
|
|
},
|
|
};
|
|
|
|
|
|
struct ioexp_map_s ioexp_map_redwood_p09p16_p25p32 = {
|
|
|
|
.chip_amount = 3,
|
|
.data_width = 2,
|
|
|
|
.map_present = { {2, 1, 0}, /* map_present[0] = MOD_ABS_PORT(X) */
|
|
{2, 1, 1}, /* map_present[1] = MOD_ABS_PORT(X+1) */
|
|
{2, 1, 2}, /* map_present[2] = MOD_ABS_PORT(X+2) */
|
|
{2, 1, 3}, /* map_present[3] = MOD_ABS_PORT(X+3) */
|
|
{2, 1, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
|
|
{2, 1, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
|
|
{2, 1, 6}, /* map_present[6] = MOD_ABS_PORT(X+6) */
|
|
{2, 1, 7}, /* map_present[7] = MOD_ABS_PORT(X+7) */
|
|
},
|
|
.map_reset = { {0, 0, 0}, /* map_reset[0] = QRESET_QSFP28_N_P(X) */
|
|
{0, 0, 1}, /* map_reset[1] = QRESET_QSFP28_N_P(X+1) */
|
|
{0, 0, 2}, /* map_reset[2] = QRESET_QSFP28_N_P(X+2) */
|
|
{0, 0, 3}, /* map_reset[3] = QRESET_QSFP28_N_P(X+3) */
|
|
{1, 0, 0}, /* map_reset[4] = QRESET_QSFP28_N_P(X+4) */
|
|
{1, 0, 1}, /* map_reset[5] = QRESET_QSFP28_N_P(X+5) */
|
|
{1, 0, 2}, /* map_reset[6] = QRESET_QSFP28_N_P(X+6) */
|
|
{1, 0, 3}, /* map_reset[7] = QRESET_QSFP28_N_P(X+7) */
|
|
},
|
|
.map_lpmod = { {0, 0, 4}, /* map_lpmod[0] = LPMODE_QSFP28_P(X) */
|
|
{0, 0, 5}, /* map_lpmod[1] = LPMODE_QSFP28_P(X+1) */
|
|
{0, 0, 6}, /* map_lpmod[2] = LPMODE_QSFP28_P(X+2) */
|
|
{0, 0, 7}, /* map_lpmod[3] = LPMODE_QSFP28_P(X+3) */
|
|
{1, 0, 4}, /* map_lpmod[4] = LPMODE_QSFP28_P(X+4) */
|
|
{1, 0, 5}, /* map_lpmod[5] = LPMODE_QSFP28_P(X+5) */
|
|
{1, 0, 6}, /* map_lpmod[6] = LPMODE_QSFP28_P(X+6) */
|
|
{1, 0, 7}, /* map_lpmod[7] = LPMODE_QSFP28_P(X+7) */
|
|
},
|
|
.map_modsel = { {0, 1, 4}, /* map_modsel[0] = MODSEL_QSFP28_N_P(X) */
|
|
{0, 1, 5}, /* map_modsel[1] = MODSEL_QSFP28_N_P(X+1) */
|
|
{0, 1, 6}, /* map_modsel[2] = MODSEL_QSFP28_N_P(X+2) */
|
|
{0, 1, 7}, /* map_modsel[3] = MODSEL_QSFP28_N_P(X+3) */
|
|
{1, 1, 4}, /* map_modsel[4] = MODSEL_QSFP28_N_P(X+4) */
|
|
{1, 1, 5}, /* map_modsel[5] = MODSEL_QSFP28_N_P(X+5) */
|
|
{1, 1, 6}, /* map_modsel[6] = MODSEL_QSFP28_N_P(X+6) */
|
|
{1, 1, 7}, /* map_modsel[7] = MODSEL_QSFP28_N_P(X+7) */
|
|
},
|
|
};
|
|
|
|
/* ========== Private functions ==========
|
|
*/
|
|
int check_channel_tier_1(void);
|
|
|
|
struct i2c_client *
|
|
_get_i2c_client(struct ioexp_obj_s *self,
|
|
int chip_id){
|
|
|
|
struct ioexp_i2c_s *i2c_curr_p = self->i2c_head_p;
|
|
|
|
if (!(i2c_curr_p)){
|
|
SWPS_ERR("%s: i2c_curr_p is NULL\n", __func__);
|
|
return NULL;
|
|
}
|
|
while (i2c_curr_p){
|
|
if ((i2c_curr_p->chip_id) == chip_id){
|
|
return i2c_curr_p->i2c_client_p;
|
|
}
|
|
i2c_curr_p = i2c_curr_p->next;
|
|
}
|
|
SWPS_ERR("%s: not exist! <chip_id>:%d\n", __func__, chip_id);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
_common_ioexp_update_one(struct ioexp_obj_s *self,
|
|
struct ioexp_addr_s *ioexp_addr,
|
|
int chip_id,
|
|
int data_width,
|
|
int show_err,
|
|
char *caller_name) {
|
|
int buf = 0;
|
|
int err = 0;
|
|
int data_id = 0;
|
|
int r_offset = 0;
|
|
|
|
for(data_id=0; data_id<data_width; data_id++){
|
|
/* Read from IOEXP */
|
|
r_offset = ioexp_addr->read_offset[data_id];
|
|
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id), r_offset);
|
|
/* Check error */
|
|
if (buf < 0) {
|
|
err = 1;
|
|
if (show_err) {
|
|
SWPS_INFO("IOEXP-%d read fail! <err>:%d \n", self->ioexp_id, buf);
|
|
SWPS_INFO("Dump: <chan>:%d <addr>:0x%02x <offset>:%d, <caller>:%s\n",
|
|
ioexp_addr->chan_id, ioexp_addr->chip_addr,
|
|
ioexp_addr->read_offset[data_id], caller_name);
|
|
}
|
|
continue;
|
|
}
|
|
/* Update IOEXP object */
|
|
self->chip_data[chip_id].data[data_id] = (uint8_t)buf;
|
|
}
|
|
if (err) {
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
common_ioexp_update_all(struct ioexp_obj_s *self,
|
|
int show_err,
|
|
char *caller_name){
|
|
|
|
int err = 0;
|
|
int chip_id = 0;
|
|
int chip_amount = self->ioexp_map_p->chip_amount;
|
|
|
|
for (chip_id=0; chip_id<chip_amount; chip_id++){
|
|
if (_common_ioexp_update_one(self,
|
|
&(self->ioexp_map_p->map_addr[chip_id]),
|
|
chip_id,
|
|
self->ioexp_map_p->data_width,
|
|
show_err,
|
|
caller_name) < 0) {
|
|
err = 1;
|
|
}
|
|
}
|
|
if (err) {
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_common_get_bit(struct ioexp_obj_s *self,
|
|
struct ioexp_bitmap_s *bitmap_obj_p,
|
|
char *func_mane){
|
|
uint8_t buf;
|
|
int err_code;
|
|
|
|
/* Get address */
|
|
err_code = self->fsm_4_direct(self);
|
|
if (err_code < 0){
|
|
return err_code;
|
|
}
|
|
|
|
if (!bitmap_obj_p){
|
|
SWPS_ERR("Layout config incorrect! <ioexp_id>:%d <func>:%s\n",
|
|
self->ioexp_id, func_mane);
|
|
return ERR_IOEXP_BADCONF;
|
|
}
|
|
/* Get data form cache */
|
|
buf = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
|
|
return (int)(buf >> bitmap_obj_p->bit_shift & 0x01);
|
|
}
|
|
|
|
|
|
static int
|
|
_common_set_bit(struct ioexp_obj_s *self,
|
|
struct ioexp_bitmap_s *bitmap_obj_p,
|
|
int input_val,
|
|
char *func_mane){
|
|
int err_code, target_offset;
|
|
uint8_t origin_byte;
|
|
uint8_t modify_byte;
|
|
|
|
/* Get address */
|
|
err_code = self->fsm_4_direct(self);
|
|
if (err_code < 0){
|
|
return err_code;
|
|
}
|
|
if (!bitmap_obj_p){
|
|
SWPS_ERR("Layout config incorrect! <ioexp>:%d <func>:%s\n",
|
|
self->ioexp_id, func_mane);
|
|
return ERR_IOEXP_BADCONF;
|
|
}
|
|
/* Prepare write date */
|
|
origin_byte = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
|
|
switch (input_val) {
|
|
case 0:
|
|
modify_byte = origin_byte;
|
|
SWP_BIT_CLEAR(modify_byte, bitmap_obj_p->bit_shift);
|
|
break;
|
|
case 1:
|
|
modify_byte = origin_byte;
|
|
SWP_BIT_SET(modify_byte, bitmap_obj_p->bit_shift);
|
|
break;
|
|
default:
|
|
SWPS_ERR("Input value incorrect! <val>:%d <ioexp>:%d <func>:%s\n",
|
|
input_val, self->ioexp_id, func_mane);
|
|
return ERR_IOEXP_BADINPUT;
|
|
}
|
|
/* Setup i2c client */
|
|
target_offset = self->ioexp_map_p->map_addr[bitmap_obj_p->chip_id].write_offset[bitmap_obj_p->ioexp_voffset];
|
|
/* Write byte to chip via I2C */
|
|
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, bitmap_obj_p->chip_id),
|
|
target_offset,
|
|
modify_byte);
|
|
/* Update or bollback object */
|
|
if (err_code < 0){
|
|
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = origin_byte;
|
|
SWPS_ERR("I2C write fail! <input>:%d <ioexp>:%d <func>:%s <err>:%d\n",
|
|
input_val, self->ioexp_id, func_mane, err_code);
|
|
return err_code;
|
|
}
|
|
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = modify_byte;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ========== Object public functions ==========
|
|
*/
|
|
int
|
|
common_get_present(struct ioexp_obj_s *self,
|
|
int virt_offset){
|
|
|
|
int UNPLUG = 1;
|
|
int retval = ERR_IOEXP_UNEXCPT;
|
|
|
|
retval = _common_get_bit(self,
|
|
&(self->ioexp_map_p->map_present[virt_offset]),
|
|
"common_get_present");
|
|
if (retval < 0) {
|
|
/* [Note]
|
|
* => Transceiver object does not need to handle IOEXP layer issues.
|
|
*/
|
|
return UNPLUG;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
int
|
|
common_get_reset(struct ioexp_obj_s *self,
|
|
int virt_offset){
|
|
|
|
return _common_get_bit(self,
|
|
&(self->ioexp_map_p->map_reset[virt_offset]),
|
|
"common_get_reset");
|
|
}
|
|
|
|
|
|
int
|
|
common_get_lpmod(struct ioexp_obj_s *self,
|
|
int virt_offset){
|
|
|
|
return _common_get_bit(self,
|
|
&(self->ioexp_map_p->map_lpmod[virt_offset]),
|
|
"common_get_lpmod");
|
|
}
|
|
|
|
|
|
int
|
|
common_get_modsel(struct ioexp_obj_s *self,
|
|
int virt_offset){
|
|
|
|
return _common_get_bit(self,
|
|
&(self->ioexp_map_p->map_modsel[virt_offset]),
|
|
"common_get_modsel");
|
|
}
|
|
|
|
int
|
|
common_set_reset(struct ioexp_obj_s *self,
|
|
int virt_offset,
|
|
int input_val){
|
|
|
|
return _common_set_bit(self,
|
|
&(self->ioexp_map_p->map_reset[virt_offset]),
|
|
input_val,
|
|
"common_set_reset");
|
|
}
|
|
|
|
|
|
int
|
|
common_set_lpmod(struct ioexp_obj_s *self,
|
|
int virt_offset,
|
|
int input_val){
|
|
|
|
return _common_set_bit(self,
|
|
&(self->ioexp_map_p->map_lpmod[virt_offset]),
|
|
input_val,
|
|
"common_set_lpmod");
|
|
}
|
|
|
|
|
|
int
|
|
common_set_modsel(struct ioexp_obj_s *self,
|
|
int virt_offset,
|
|
int input_val){
|
|
|
|
return _common_set_bit(self,
|
|
&(self->ioexp_map_p->map_modsel[virt_offset]),
|
|
input_val,
|
|
"common_set_modsel");
|
|
}
|
|
|
|
int
|
|
ioexp_get_not_support(struct ioexp_obj_s *self,
|
|
int virt_offset){
|
|
return ERR_IOEXP_NOTSUPPORT;
|
|
}
|
|
|
|
|
|
int
|
|
ioexp_set_not_support(struct ioexp_obj_s *self,
|
|
int virt_offset,
|
|
int input_val){
|
|
return ERR_IOEXP_NOTSUPPORT;
|
|
}
|
|
|
|
/* ========== Initial functions for IO Expander ==========
|
|
*/
|
|
int
|
|
common_ioexp_init(struct ioexp_obj_s *self) {
|
|
|
|
int chip_id, offset, err_code;
|
|
struct ioexp_addr_s *addr_p;
|
|
|
|
if (self->mode == IOEXP_MODE_DIRECT) { ///important
|
|
goto update_common_ioexp_init;
|
|
}
|
|
/* Setup default value to each physical IO Expander */
|
|
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
|
|
/* Get address mapping */
|
|
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
|
|
if (!addr_p){
|
|
SWPS_ERR("%s: IOEXP config incorrect! <chip_id>:%d \n",
|
|
__func__, chip_id);
|
|
return -1;
|
|
}
|
|
/* Setup default value */
|
|
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
|
|
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
|
|
addr_p->write_offset[offset],
|
|
addr_p->data_default[offset]);
|
|
if (err_code < 0){
|
|
SWPS_ERR("%s: set default fail! <error>:%d \n",
|
|
__func__, err_code);
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
}
|
|
}
|
|
|
|
update_common_ioexp_init:
|
|
/* Check and update info to object */
|
|
err_code = self->update_all(self, 1, "common_ioexp_init");
|
|
if (err_code < 0) {
|
|
SWPS_ERR("%s: update_all() fail! <error>:%d \n",
|
|
__func__, err_code);
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ========== Object functions for Final State Machine ==========
|
|
*/
|
|
int
|
|
_is_channel_ready(struct ioexp_obj_s *self){
|
|
|
|
int buf = 0;
|
|
int chip_id = 0; /* Use first chip which be registered */
|
|
int data_id = 0; /* Use first byte which be registered */
|
|
struct ioexp_addr_s *ioexp_addr = NULL;
|
|
|
|
ioexp_addr = &(self->ioexp_map_p->map_addr[chip_id]);
|
|
if (!ioexp_addr){
|
|
SWPS_ERR("%s: config incorrect!\n", __func__);
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id),
|
|
ioexp_addr->read_offset[data_id]);
|
|
if (buf >= 0){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_ioexp_init_handler(struct ioexp_obj_s *self){
|
|
|
|
int return_val;
|
|
|
|
switch (self->mode) {
|
|
case IOEXP_MODE_DIRECT:
|
|
return_val = self->init(self);
|
|
if (return_val < 0){
|
|
self->state = STATE_IOEXP_ABNORMAL;
|
|
} else {
|
|
self->state = STATE_IOEXP_NORMAL;
|
|
}
|
|
return return_val;
|
|
default:
|
|
break;
|
|
}
|
|
SWPS_ERR("%s: exception occur <mode>:%d\n", __func__, self->mode);
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
|
|
|
|
int
|
|
common_ioexp_fsm_4_direct(struct ioexp_obj_s *self){
|
|
|
|
int result_val;
|
|
int show_err = 1;
|
|
char *func_mane = "common_ioexp_fsm_4_direct";
|
|
|
|
switch (self->state){
|
|
case STATE_IOEXP_INIT:
|
|
result_val = _ioexp_init_handler(self);
|
|
/* Exception case: terminate initial procedure */
|
|
if(result_val < 0){
|
|
/* Initial fail */
|
|
return result_val;
|
|
}
|
|
if(self->state == STATE_IOEXP_INIT){
|
|
/* Keep in INIT state, and return error */
|
|
return ERR_IOEXP_UNINIT;
|
|
}
|
|
/* Case: Initial done */
|
|
return 0;
|
|
|
|
case STATE_IOEXP_NORMAL:
|
|
result_val = self->update_all(self, show_err, func_mane);
|
|
if (result_val < 0){
|
|
SWPS_INFO("%s: NORMAL -> ABNORMAL <err>:%d\n",
|
|
__func__, result_val);
|
|
self->state = STATE_IOEXP_ABNORMAL;
|
|
return result_val;
|
|
}
|
|
self->state = STATE_IOEXP_NORMAL;
|
|
return 0;
|
|
|
|
case STATE_IOEXP_ABNORMAL:
|
|
result_val = self->update_all(self, show_err, func_mane);
|
|
if (result_val < 0){
|
|
self->state = STATE_IOEXP_ABNORMAL;
|
|
return result_val;
|
|
}
|
|
SWPS_DEBUG("%s: ABNORMAL -> NORMAL <err>:%d\n",
|
|
__func__, result_val);
|
|
self->state = STATE_IOEXP_NORMAL;
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
SWPS_ERR("%s: Exception occurs <state>:%d\n",
|
|
__func__, self->state);
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
|
|
/* ========== Functions for Factory pattern ==========
|
|
*/
|
|
static struct ioexp_map_s *
|
|
get_ioexp_map(int ioexp_type){
|
|
switch (ioexp_type){
|
|
case IOEXP_TYPE_REDWOOD_P01P08:
|
|
return &ioexp_map_redwood_p01p08_p17p24;
|
|
case IOEXP_TYPE_REDWOOD_P09P16:
|
|
return &ioexp_map_redwood_p09p16_p25p32;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
setup_ioexp_ssize_attr(struct ioexp_obj_s *self,
|
|
struct ioexp_map_s *ioexp_map_p,
|
|
int ioexp_id,
|
|
int ioexp_type,
|
|
int run_mode){
|
|
switch (run_mode){
|
|
case IOEXP_MODE_DIRECT: /* Direct access device mode */
|
|
self->mode = run_mode;
|
|
break;
|
|
default:
|
|
SWPS_ERR("%s: non-defined run_mode:%d\n",
|
|
__func__, run_mode);
|
|
self->mode = ERR_IOEXP_UNEXCPT;
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
self->ioexp_id = ioexp_id;
|
|
self->ioexp_type = ioexp_type;
|
|
self->ioexp_map_p = ioexp_map_p;
|
|
self->state = STATE_IOEXP_INIT;
|
|
mutex_init(&self->lock);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_addr_mapping(struct ioexp_obj_s *self,
|
|
struct ioexp_addr_s *addr_map_p){
|
|
if (!addr_map_p){
|
|
SWPS_ERR("%s: map is null\n", __func__);
|
|
return -1;
|
|
}
|
|
self->ioexp_map_p->map_addr = addr_map_p;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_ioexp_public_cb(struct ioexp_obj_s *self,
|
|
int ioexp_type){
|
|
switch (ioexp_type){
|
|
case IOEXP_TYPE_REDWOOD_P01P08:
|
|
case IOEXP_TYPE_REDWOOD_P09P16:
|
|
self->get_present = common_get_present;
|
|
self->get_reset = common_get_reset;
|
|
self->get_lpmod = common_get_lpmod;
|
|
self->get_modsel = common_get_modsel;
|
|
self->set_reset = common_set_reset;
|
|
self->set_lpmod = common_set_lpmod;
|
|
self->set_modsel = common_set_modsel;
|
|
return 0;
|
|
default:
|
|
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
|
|
break;
|
|
}
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_ioexp_private_cb(struct ioexp_obj_s *self,
|
|
int ioexp_type){
|
|
|
|
switch (ioexp_type){
|
|
case IOEXP_TYPE_REDWOOD_P01P08:
|
|
case IOEXP_TYPE_REDWOOD_P09P16:
|
|
|
|
self->init = common_ioexp_init;
|
|
self->update_all = common_ioexp_update_all;
|
|
self->fsm_4_direct = common_ioexp_fsm_4_direct;
|
|
return 0;
|
|
|
|
default:
|
|
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
|
|
break;
|
|
}
|
|
return ERR_IOEXP_UNEXCPT;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_i2c_client_one(struct ioexp_obj_s *self,
|
|
int chip_id){
|
|
|
|
char *err_msg = "ERROR";
|
|
struct i2c_adapter *adap = NULL;
|
|
struct i2c_client *client = NULL;
|
|
struct ioexp_i2c_s *i2c_obj_p = NULL;
|
|
struct ioexp_i2c_s *i2c_curr_p = NULL;
|
|
|
|
int chan_id = self->ioexp_map_p->map_addr[chip_id].chan_id;
|
|
adap = i2c_get_adapter(chan_id);
|
|
if(!adap){
|
|
err_msg = "Can not get adap!";
|
|
goto err_ioexp_setup_i2c_1;
|
|
}
|
|
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
|
if (!client){
|
|
err_msg = "Can not kzalloc client!";
|
|
goto err_ioexp_setup_i2c_1;
|
|
}
|
|
i2c_obj_p = kzalloc(sizeof(*i2c_obj_p), GFP_KERNEL);
|
|
if (!i2c_obj_p){
|
|
err_msg = "Can not kzalloc i2c_obj_p!";
|
|
goto err_ioexp_setup_i2c_2;
|
|
}
|
|
client->adapter = adap;
|
|
client->addr = self->ioexp_map_p->map_addr[chip_id].chip_addr;
|
|
i2c_obj_p->i2c_client_p = client;
|
|
i2c_obj_p->chip_id = chip_id;
|
|
i2c_obj_p->next = NULL;
|
|
if (!self->i2c_head_p){
|
|
self->i2c_head_p = i2c_obj_p;
|
|
} else {
|
|
i2c_curr_p = self->i2c_head_p;
|
|
while (i2c_curr_p->next){
|
|
i2c_curr_p = i2c_curr_p->next;
|
|
}
|
|
i2c_curr_p->next = i2c_obj_p;
|
|
}
|
|
return 0;
|
|
|
|
err_ioexp_setup_i2c_2:
|
|
kfree(client);
|
|
err_ioexp_setup_i2c_1:
|
|
SWPS_ERR("%s: %s <chanID>:%d\n", __func__, err_msg, chan_id);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_i2c_client(struct ioexp_obj_s* self){
|
|
|
|
int result;
|
|
int chip_id = 0;
|
|
|
|
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
|
|
result = setup_i2c_client_one(self, chip_id);
|
|
if (result < 0){
|
|
SWPS_ERR("%s fail! <chip_id>:%d\n", __func__, chip_id);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
setup_ioexp_config(struct ioexp_obj_s *self) {
|
|
|
|
int chip_id, offset, err_code;
|
|
struct ioexp_addr_s *addr_p;
|
|
|
|
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
|
|
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
|
|
if (!addr_p){
|
|
SWPS_ERR("IOEXP config incorrect! <chip_id>:%d \n",chip_id);
|
|
return -1;
|
|
}
|
|
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
|
|
|
|
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
|
|
addr_p->conf_offset[offset],
|
|
addr_p->conf_default[offset]);
|
|
|
|
if (err_code < 0){
|
|
SWPS_INFO("%s: set conf fail! <err>:%d \n", __func__, err_code);
|
|
return -2;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct ioexp_obj_s *
|
|
_create_ioexp_obj(int ioexp_id,
|
|
int ioexp_type,
|
|
struct ioexp_addr_s *addr_map_p,
|
|
int run_mode){
|
|
|
|
struct ioexp_map_s* ioexp_map_p;
|
|
struct ioexp_obj_s* result_p;
|
|
struct ioexp_i2c_s *i2c_curr_p;
|
|
struct ioexp_i2c_s *i2c_next_p;
|
|
|
|
/* Get layout */
|
|
ioexp_map_p = get_ioexp_map(ioexp_type);
|
|
if (!ioexp_map_p){
|
|
SWPS_ERR("%s: Invalid ioexp_type\n", __func__);
|
|
goto err_create_ioexp_fail;
|
|
}
|
|
/* Prepare IOEXP object */
|
|
result_p = kzalloc(sizeof(*result_p), GFP_KERNEL);
|
|
if (!result_p){
|
|
SWPS_ERR("%s: kzalloc failure!\n", __func__);
|
|
goto err_create_ioexp_fail;
|
|
}
|
|
/* Prepare static size attributes */
|
|
if (setup_ioexp_ssize_attr(result_p,
|
|
ioexp_map_p,
|
|
ioexp_id,
|
|
ioexp_type,
|
|
run_mode) < 0){
|
|
goto err_create_ioexp_setup_attr_fail;
|
|
}
|
|
/* Prepare address mapping */
|
|
if (setup_addr_mapping(result_p, addr_map_p) < 0){
|
|
goto err_create_ioexp_setup_attr_fail;
|
|
}
|
|
if (setup_i2c_client(result_p) < 0){
|
|
goto err_create_ioexp_setup_i2c_fail;
|
|
}
|
|
/* Prepare call back functions of object */
|
|
if (setup_ioexp_public_cb(result_p, ioexp_type) < 0){
|
|
goto err_create_ioexp_setup_i2c_fail;
|
|
}
|
|
if (setup_ioexp_private_cb(result_p, ioexp_type) < 0){
|
|
goto err_create_ioexp_setup_i2c_fail;
|
|
}
|
|
return result_p;
|
|
|
|
err_create_ioexp_setup_i2c_fail:
|
|
i2c_curr_p = result_p->i2c_head_p;
|
|
i2c_next_p = result_p->i2c_head_p;
|
|
while (i2c_curr_p){
|
|
i2c_next_p = i2c_curr_p->next;
|
|
kfree(i2c_curr_p->i2c_client_p);
|
|
kfree(i2c_curr_p);
|
|
i2c_curr_p = i2c_next_p;
|
|
}
|
|
err_create_ioexp_setup_attr_fail:
|
|
kfree(result_p);
|
|
err_create_ioexp_fail:
|
|
SWPS_ERR("%s: fail! <id>:%d <type>:%d \n",
|
|
__func__, ioexp_id, ioexp_type);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
create_ioexp_obj(int ioexp_id,
|
|
int ioexp_type,
|
|
struct ioexp_addr_s *addr_map_p,
|
|
int run_mode){
|
|
|
|
struct ioexp_obj_s *ioexp_p = NULL;
|
|
|
|
ioexp_p = _create_ioexp_obj(ioexp_id, ioexp_type,
|
|
addr_map_p, run_mode);
|
|
if (!ioexp_p){
|
|
return -1;
|
|
}
|
|
if (ioexp_head_p == NULL){
|
|
ioexp_head_p = ioexp_p;
|
|
ioexp_tail_p = ioexp_p;
|
|
return 0;
|
|
}
|
|
ioexp_tail_p->next = ioexp_p;
|
|
ioexp_tail_p = ioexp_p;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_init_ioexp_obj(struct ioexp_obj_s* self) {
|
|
|
|
char *err_msg = "ERR";
|
|
char *func_name = "_init_ioexp_obj";
|
|
|
|
/* Setup IOEXP configure byte */
|
|
if (setup_ioexp_config(self) < 0){
|
|
err_msg = "setup_ioexp_config fail";
|
|
goto err_init_ioexp_obj;
|
|
}
|
|
/* Setup default data */
|
|
if (_ioexp_init_handler(self) < 0){
|
|
err_msg = "_ioexp_init_handler fail";
|
|
goto err_init_ioexp_obj;
|
|
}
|
|
/* Update all */
|
|
if (self->state == STATE_IOEXP_NORMAL){
|
|
if (self->update_all(self, 1, func_name) < 0){
|
|
err_msg = "update_all() fail";
|
|
goto err_init_ioexp_obj;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
err_init_ioexp_obj:
|
|
SWPS_DEBUG("%s: %s\n", __func__, err_msg);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
init_ioexp_objs(void){
|
|
/* Return value:
|
|
* 0: Success
|
|
* -1: Detect topology error
|
|
* -2: SWPS internal error
|
|
*/
|
|
|
|
struct ioexp_obj_s *curr_p = ioexp_head_p;
|
|
|
|
if (!curr_p) {
|
|
SWPS_ERR("%s: ioexp_head_p is NULL\n", __func__);
|
|
return -2;
|
|
}
|
|
while (curr_p) {
|
|
if (_init_ioexp_obj(curr_p) < 0) {
|
|
SWPS_DEBUG("%s: _init_ioexp_obj() fail\n", __func__);
|
|
return -1;
|
|
}
|
|
curr_p = curr_p->next;
|
|
}
|
|
SWPS_DEBUG("%s: done.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
clean_ioexp_objs(void){
|
|
|
|
struct ioexp_i2c_s *i2c_curr_p = NULL;
|
|
struct ioexp_i2c_s *i2c_next_p = NULL;
|
|
struct ioexp_obj_s *ioexp_next_p = NULL;
|
|
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
|
|
|
|
if (ioexp_head_p == NULL){
|
|
ioexp_tail_p = NULL;
|
|
return;
|
|
}
|
|
while(ioexp_curr_p){
|
|
ioexp_next_p = ioexp_curr_p->next;
|
|
i2c_curr_p = ioexp_curr_p->i2c_head_p;
|
|
while (i2c_curr_p) {
|
|
i2c_next_p = i2c_curr_p->next;
|
|
kfree(i2c_curr_p->i2c_client_p);
|
|
kfree(i2c_curr_p);
|
|
i2c_curr_p = i2c_next_p;
|
|
}
|
|
kfree(ioexp_curr_p);
|
|
ioexp_curr_p = ioexp_next_p;
|
|
}
|
|
ioexp_tail_p = NULL;
|
|
SWPS_DEBUG("%s: done.\n", __func__);
|
|
}
|
|
|
|
struct ioexp_obj_s *
|
|
get_ioexp_obj(int ioexp_id){
|
|
|
|
struct ioexp_obj_s *result_p = NULL;
|
|
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
|
|
|
|
while(ioexp_curr_p){
|
|
if (ioexp_curr_p->ioexp_id == ioexp_id){
|
|
result_p = ioexp_curr_p;
|
|
break;
|
|
}
|
|
ioexp_curr_p = ioexp_curr_p->next;
|
|
}
|
|
return result_p;
|
|
}
|
|
int
|
|
check_channel_tier_1(void) {
|
|
|
|
if ( (!_is_channel_ready(ioexp_head_p)) &&
|
|
(!_is_channel_ready(ioexp_tail_p)) ){
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|