sonic-buildimage/platform/broadcom/sonic-platform-modules-inventec/d6356/modules/inv_mux.c
David Xiao 110bff9b47 [Inventec][D6356] Update driver and Add new platform API implementation (#3521)
* Update driver and Add new platform API implementation for Inventec D6356
* Update Platform API (SFP)
* Update Platform API (QSFP)
* Update Platform API (FAN, THERMAL)
Signed-off-by: David Xiao <xiao.david@inventec.com>
2019-10-09 16:16:05 -07:00

548 lines
13 KiB
C

#include <asm/io.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include "io_expander.h"
#include "inv_mux.h"
/* For build single module using (Ex: ONL platform) */
#include <linux/module.h>
//#include <linux/inventec/d5254/io_expander.h>
//#include <linux/inventec/d5254/inv_mux.h>
static struct mux_obj_s *mux_head_p = NULL;
/* ========== MUX object functions ==========
*/
static int
_setup_i2c_value(struct mux_obj_s *self, int offset, int value){
return i2c_smbus_write_byte_data(self->i2c_client_p, offset, value);
}
static int
_setup_i2c_client(struct mux_obj_s *self, int chan_id, int addr){
struct i2c_adapter *adap = NULL;
char *emsg = "ERR";
adap = i2c_get_adapter(chan_id);
if (!adap){
emsg = "can't get adapter";
goto err_setup_i2c_client;
}
self->i2c_client_p = kzalloc(sizeof(*self->i2c_client_p), GFP_KERNEL);
if (!self->i2c_client_p){
emsg = "can't kzalloc client";
goto err_setup_i2c_client;
}
self->i2c_client_p->adapter = adap;
self->i2c_client_p->addr = addr;
return 0;
err_setup_i2c_client:
SWPS_ERR("%s: %s\n", __func__, emsg);
return ERR_MUX_UNEXCPT;
}
int
_common_force_pull_gpio(int mem_addr,
int input,
int bit_offset){
unsigned int val = 0;
unsigned int targ = 0;
/* Get current value */
val = inl(mem_addr);
if (val == 0) {
SWPS_ERR("%s: inl:%d fail!\n", __func__, val);
return -1;
}
/* Count target value */
switch (input) {
case 0: /* Pull Low */
targ = (val & (~(1 << bit_offset)));
break;
case 1: /* Pull high */
targ = (val | (1 << bit_offset));
break;
default:
SWPS_ERR("%s: input state:%d incorrect!\n",
__func__, input);
return -1;
}
/* Setup gpio */
outl(targ, mem_addr);
if (targ != inl(mem_addr)){
SWPS_ERR("%s: outl:%d fail!\n", __func__, targ);
return -1;
}
SWPS_DEBUG("%s: done.\n", __func__);
return 0;
}
int
rangeley_force_pull_high(struct mux_obj_s *self){
SWPS_ERR("%s: not ready!\n", __func__);
return -1;
}
int
rangeley_force_pull_low(struct mux_obj_s *self){
SWPS_ERR("%s: not ready!\n", __func__);
return -1;
}
int
hedera_force_pull_high(struct mux_obj_s *self){
return _common_force_pull_gpio(MUX_RST_MEM_ADDR_HEDERA, 1, 5);
}
int
hedera_force_pull_low(struct mux_obj_s *self){
return _common_force_pull_gpio(MUX_RST_MEM_ADDR_HEDERA, 0, 5);
}
int
normal_gpio_pull_high(struct mux_obj_s *self){
return gpio_direction_output(self->gpio_num, 1);
}
int
normal_gpio_pull_low(struct mux_obj_s *self){
return gpio_direction_output(self->gpio_num, 0);
}
int
cpld_rst_all_4_pull_low(struct mux_obj_s *self){
char *emsg = "ERR";
int err = ERR_MUX_UNEXCPT;
switch(self->gpio_num) {
case MUX_RST_CPLD_C0_A77_70_74_RST_ALL:
goto setlow_cpld_rst_all_4_c0_a77_70_74_rst_all;
default:
break;
}
emsg = "Undefined case";
goto err_cpld_rst_all_4_pull_low;
setlow_cpld_rst_all_4_c0_a77_70_74_rst_all:
err = _setup_i2c_value(self, 0x70, 0x0);
if (err < 0) {
emsg = "setup 0x70 fail";
goto err_cpld_rst_all_4_pull_low;
}
err = _setup_i2c_value(self, 0x74, 0x01);
if (err < 0) {
emsg = "setup 0x74 fail";
goto err_cpld_rst_all_4_pull_low;
}
return 0;
err_cpld_rst_all_4_pull_low:
SWPS_INFO("%s: %s <type>:%d <err>:%d\n",
__func__, emsg, self->gpio_num, err);
return ERR_MUX_UNEXCPT;
}
int
cpld_rst_all_4_pull_high(struct mux_obj_s *self){
char *emsg = "ERR";
int err = ERR_MUX_UNEXCPT;
switch(self->gpio_num) {
case MUX_RST_CPLD_C0_A77_70_74_RST_ALL:
goto sethigh_cpld_rst_all_4_c0_a77_70_74_rst_all;
default:
break;
}
emsg = "Undefined case";
goto err_cpld_rst_all_4_pull_high;
sethigh_cpld_rst_all_4_c0_a77_70_74_rst_all:
err = _setup_i2c_value(self, 0x70, 0xfe);
if (err < 0) {
emsg = "setup 0x70 fail";
goto err_cpld_rst_all_4_pull_high;
}
err = _setup_i2c_value(self, 0x74, 0x03);
if (err < 0) {
emsg = "setup 0x74 fail";
goto err_cpld_rst_all_4_pull_high;
}
return 0;
err_cpld_rst_all_4_pull_high:
SWPS_INFO("%s: %s <type>:%d <err>:%d\n",
__func__, emsg, self->gpio_num, err);
return ERR_MUX_UNEXCPT;
}
int
pca9548_reset_mux_all(struct mux_obj_s *self){
/* [Note] Power-on reset (PCA9548A-NXP)
* When power is applied to VDD, an internal Power-On Reset (POR)
* holds the PCA9548A in a reset condition until VDD has reached
* VPOR. At this point, the reset condition is released and the
* PCA9548A register and I2C-bus state machine are initialized to
* their default states (all zeroes) causing all the channels to
* be deselected. Thereafter, VDD must be lowered below 0.2 V for
* at least 5 us in order to reset the device.
*/
if (self->_pull_low(self) < 0) {
SWPS_ERR("%s: _pull_low fail!\n", __func__);
return -1;
}
mdelay(MUX_RST_WAIT_MS_PCA9548);
if (self->_pull_high(self) < 0) {
SWPS_ERR("%s: _pull_high fail!\n", __func__);
return -1;
}
mdelay(MUX_RST_WAIT_MS_PCA9548);
return 0;
}
int
cpld_reset_mux_all(struct mux_obj_s *self){
char *emsg = "ERR";
int err = ERR_MUX_UNEXCPT;
switch(self->gpio_num) {
case MUX_RST_CPLD_C0_A77_70_74_RST_ALL:
goto reset_cpld_rst_all_4_c0_a77_70_74_rst_all;
default:
break;
}
emsg = "Undefined case";
goto err_cpld_reset_mux_all;
reset_cpld_rst_all_4_c0_a77_70_74_rst_all:
if (self->_pull_low(self) < 0) {
emsg = "_pull_low fail";
goto err_cpld_reset_mux_all;
}
mdelay(MUX_RST_WAIT_MS_CPLD);
return 0;
err_cpld_reset_mux_all:
SWPS_INFO("%s: %s <type>:%d <err>:%d\n",
__func__, emsg, self->gpio_num, err);
return ERR_MUX_UNEXCPT;
}
int
common_reset_mux_all(struct mux_obj_s *self){
SWPS_ERR("%s: not ready!\n", __func__);
return -1;
}
int
init_gpio_4_force(struct mux_obj_s *self){
if (self->_pull_high(self) < 0) {
SWPS_ERR("%s: setup default fail!\n", __func__);
return -1;
}
return 0;
}
int
init_gpio_4_normal(struct mux_obj_s *self){
int err = 0;
char *emsg = "ERR";
if (!gpio_is_valid(self->gpio_num)) {
emsg = "GPIO invalid";
goto err_init_gpio_4_normal;
}
err = gpio_request(self->gpio_num, MUX_GPIO_LABEL);
if (err < 0) {
emsg = "gpio_request fail";
goto err_init_gpio_4_normal;
}
err = self->_pull_high(self);
if (err < 0) {
emsg = "setup default fail";
goto err_init_gpio_4_normal;
}
SWPS_DEBUG("%s: gpio_request:%d ok.\n", __func__, self->gpio_num);
return 0;
err_init_gpio_4_normal:
SWPS_ERR("%s: %s <gpio>:%d <err>:%d\n",
__func__, emsg, self->gpio_num, err);
return -1;
}
int
init_cpld_4_rst_all(struct mux_obj_s *self){
char *emsg = "ERR";
int err = ERR_MUX_UNEXCPT;
int chan = ERR_MUX_UNEXCPT;
int addr = ERR_MUX_UNEXCPT;
switch(self->gpio_num) {
case MUX_RST_CPLD_C0_A77_70_74_RST_ALL:
goto init_cpld_i2c_c0_a77_70_74_rst_all;
default:
break;
}
emsg = "Undefined case";
goto err_init_cpld_4_rst_all;
init_cpld_i2c_c0_a77_70_74_rst_all:
chan = 0;
addr = 0x77;
err = _setup_i2c_client(self, chan, addr);
if (err < 0) {
emsg = "_setup_i2c_client fail";
goto err_init_cpld_4_rst_all;
}
err = self->_pull_high(self);
if (err < 0) {
emsg = "setup default value fail";
goto err_init_cpld_4_rst_all;
}
SWPS_DEBUG("%s: init_cpld_i2c_c0_a77_70_74_rst_all ok", __func__);
return 0;
err_init_cpld_4_rst_all:
SWPS_INFO("%s: %s <type>:%d <err>:%d\n",
__func__, emsg, self->gpio_num, err);
return ERR_MUX_UNEXCPT;
}
int
clean_gpio_4_common(struct mux_obj_s *self){
if (!self) return 0;
if (!gpio_is_valid(self->gpio_num)) return 0;
self->_pull_high(self);
gpio_free(mux_head_p->gpio_num);
return 0;
}
int
clean_cpld_4_rst_all(struct mux_obj_s *self){
if (!self) return 0;
self->_pull_high(self);
if (self->i2c_client_p) {
i2c_put_adapter(self->i2c_client_p->adapter);
kfree(self->i2c_client_p);
}
return 0;
}
static int
_setup_muxctl_cb(struct mux_obj_s *self,
unsigned gpio){
char mod_dsc[32] = "ERR";
switch (gpio) {
case MUX_RST_GPIO_FORCE_RANGELEY:
self->gpio_num = gpio;
self->_pull_low = rangeley_force_pull_low;
self->_pull_high = rangeley_force_pull_high;
self->_init = init_gpio_4_force;
self->_clean = clean_gpio_4_common;
self->reset = pca9548_reset_mux_all;
memset(mod_dsc, 0, 32);
snprintf(mod_dsc, 31, "Rangeley force mode");
goto ok_setup_muxctl_cb;
case MUX_RST_GPIO_FORCE_HEDERA:
self->gpio_num = gpio;
self->_pull_low = hedera_force_pull_low;
self->_pull_high = hedera_force_pull_high;
self->_init = init_gpio_4_force;
self->_clean = clean_gpio_4_common;
self->reset = pca9548_reset_mux_all;
memset(mod_dsc, 0, 32);
snprintf(mod_dsc, 31, "Hedera force mode");
goto ok_setup_muxctl_cb;
case MUX_RST_GPIO_48_PCA9548:
case MUX_RST_GPIO_69_PCA9548:
case MUX_RST_GPIO_249_PCA9548:
case MUX_RST_GPIO_500_PCA9548:
case MUX_RST_GPIO_505_PCA9548:
self->gpio_num = gpio;
self->_pull_low = normal_gpio_pull_low;
self->_pull_high = normal_gpio_pull_high;
self->_init = init_gpio_4_normal;
self->_clean = clean_gpio_4_common;
self->reset = pca9548_reset_mux_all;
memset(mod_dsc, 0, 32);
snprintf(mod_dsc, 31, "Normal mode <gpio>:%d", (int)gpio);
goto ok_setup_muxctl_cb;
case MUX_RST_CPLD_C0_A77_70_74_RST_ALL:
self->gpio_num = gpio;
self->_pull_low = cpld_rst_all_4_pull_low;
self->_pull_high = cpld_rst_all_4_pull_high;
self->_init = init_cpld_4_rst_all;
self->_clean = clean_cpld_4_rst_all;
self->reset = cpld_reset_mux_all;
memset(mod_dsc, 0, 32);
snprintf(mod_dsc, 31, "CPLD mode <type>:%d", (int)gpio);
goto ok_setup_muxctl_cb;
default:
break;
}
SWPS_ERR("%s: Unexpected GPIO:%d\n", __func__, gpio);
return -1;
ok_setup_muxctl_cb:
SWPS_INFO("muxctl: %s.\n", mod_dsc);
return 0;
}
/* ========== MUX public functions ==========
*/
void
clean_mux_objs(void){
struct mux_obj_s *curr_p = mux_head_p;
struct mux_obj_s *next_p = NULL;
if (!curr_p) {
SWPS_DEBUG("%s: mux_head_p is NULL\n", __func__);
return;
}
while (curr_p) {
next_p = curr_p->next;
curr_p->_clean(curr_p);
kfree(curr_p);
curr_p = next_p;
}
SWPS_DEBUG("%s: done.\n", __func__);
}
EXPORT_SYMBOL(clean_mux_objs);
int
reset_mux_objs(void){
if (!mux_head_p) {
SWPS_ERR("%s: MUX ctl object doesn't exist!\n", __func__);
return -1;
}
if (mux_head_p->reset(mux_head_p) < 0){
SWPS_ERR("%s: reset fail!\n", __func__);
return -1;
}
return 0;
}
EXPORT_SYMBOL(reset_mux_objs);
struct mux_obj_s *
_create_mux_obj(unsigned gpio){
char *emsg = "ERR";
struct mux_obj_s *obj_p = NULL;
obj_p = kzalloc(sizeof(struct mux_obj_s), GFP_KERNEL);
if (!obj_p) {
emsg = "kzalloc fail!";
goto err_create_mux_obj_1;
}
if (_setup_muxctl_cb(obj_p, gpio) < 0){
emsg = "_setup_muxctl_cb fail!";
goto err_create_mux_obj_2;
}
if (obj_p->_init(obj_p) < 0) {
emsg = "_init() fail!";
goto err_create_mux_obj_2;
}
SWPS_DEBUG("%s: created MUX object <id>:%d\n", __func__, gpio);
return obj_p;
err_create_mux_obj_2:
kfree(obj_p);
err_create_mux_obj_1:
SWPS_ERR("%s: %s <type>:%d\n", __func__, emsg, gpio);
return NULL;
}
int
init_mux_objs(unsigned gpio){
struct mux_obj_s *curr_p = NULL;
char *emsg = "ERR";
/* Create MUX control object */
if (mux_head_p) {
SWPS_DEBUG("%s: mux_head_p is not NULL!\n", __func__);
clean_mux_objs();
}
/* Currently, it is using single muxctl architecture.
* In the future, it may use the multi-muxctl.
* (Ex: Gulmohar's advance I2C control / Peony's reset single mux)
*/
curr_p = _create_mux_obj(gpio);
if (!curr_p) {
emsg = "_create_mux_obj fail";
goto err_init_mux_objs;
}
curr_p->next = NULL;
mux_head_p = curr_p;
SWPS_DEBUG("%s: all done. <type>:%d\n", __func__, gpio);
return 0;
err_init_mux_objs:
clean_mux_objs();
SWPS_ERR("%s: %s\n", __func__, emsg);
return -1;
}
EXPORT_SYMBOL(init_mux_objs);
/* For single ko module
* => You need to declare MODULE_LICENSE If you want to build single module along.
* => Ex: For ONL platform
*/
MODULE_LICENSE("GPL");