| /* |
| * driver/ccic/s2mm005-i2c.c - S2MM005 USBPD I2C device driver |
| * |
| * Copyright (C) 2017 Samsung Electronics |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| #include <linux/ccic/s2mm005_usbpd.h> |
| |
| int s2mm005_read_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size) |
| { |
| int ret, i2c_retry; u8 wbuf[2]; |
| struct i2c_msg msg[2]; |
| struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); |
| |
| mutex_lock(&usbpd_data->i2c_mutex); |
| i2c_retry = 0; |
| msg[0].addr = i2c->addr; |
| msg[0].flags = i2c->flags; |
| msg[0].len = 2; |
| msg[0].buf = wbuf; |
| msg[1].addr = i2c->addr; |
| msg[1].flags = I2C_M_RD; |
| msg[1].len = size; |
| msg[1].buf = val; |
| |
| wbuf[0] = (reg & 0xFF00) >> 8; |
| wbuf[1] = (reg & 0xFF); |
| |
| do { |
| ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg)); |
| } while (ret < 0 && i2c_retry++ < 5); |
| |
| if (ret < 0) |
| dev_err(&i2c->dev, "i2c read16 fail reg:0x%x error %d\n", reg, ret); |
| |
| mutex_unlock(&usbpd_data->i2c_mutex); |
| |
| return ret; |
| } |
| |
| int s2mm005_write_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size) |
| { |
| int ret, i2c_retry; u8 buf[258] = {0,}; |
| struct i2c_msg msg[1]; |
| struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c); |
| |
| if (size > 256) { |
| pr_err("I2C error, over the size %d", size); |
| return -EIO; |
| } |
| |
| mutex_lock(&usbpd_data->i2c_mutex); |
| i2c_retry = 0; |
| msg[0].addr = i2c->addr; |
| msg[0].flags = 0; |
| msg[0].len = size+2; |
| msg[0].buf = buf; |
| |
| buf[0] = (reg & 0xFF00) >> 8; |
| buf[1] = (reg & 0xFF); |
| memcpy(&buf[2], val, size); |
| |
| do { |
| ret = i2c_transfer(i2c->adapter, msg, 1); |
| } while (ret < 0 && i2c_retry++ < 5); |
| |
| if (ret < 0) |
| dev_err(&i2c->dev, "i2c write fail reg:0x%x error %d\n", reg, ret); |
| |
| mutex_unlock(&usbpd_data->i2c_mutex); |
| |
| return ret; |
| } |
| |
| void s2mm005_sel_write(struct i2c_client *i2c, u16 reg, u8 *buf, i2c_slv_cmd_t cmd) |
| { |
| u8 W_DATA[8] = {0,}; |
| |
| switch (cmd) { |
| case SEL_WRITE_BYTE: |
| case SEL_WRITE_WORD: |
| case SEL_WRITE_LONG: |
| break; |
| default: |
| dev_err(&i2c->dev, "%s, cmd is worng cmd(%d)\n", __func__, cmd); |
| return; |
| } |
| |
| W_DATA[0] = 0x02; |
| W_DATA[1] = cmd; |
| W_DATA[2] = reg & 0xFF; |
| W_DATA[3] = (reg & 0xFF00) >> 8; |
| memcpy(&W_DATA[4], buf, cmd); |
| |
| s2mm005_write_byte(i2c, REG_I2C_SLV_CMD, W_DATA, cmd + 4); |
| } |
| |
| void s2mm005_sel_read(struct i2c_client *i2c, u16 reg, u8 *buf, i2c_slv_cmd_t cmd) |
| { |
| u8 W_DATA[4] = {0,}; |
| u8 size; |
| |
| switch (cmd) { |
| case SEL_READ_BYTE: |
| case SEL_READ_WORD: |
| case SEL_READ_LONG: |
| size = cmd >> 8; |
| break; |
| default: |
| dev_err(&i2c->dev, "%s, cmd is worng cmd(%d)\n", __func__, cmd); |
| return; |
| } |
| |
| W_DATA[0] = 0x02; |
| W_DATA[1] = cmd; |
| W_DATA[2] = reg & 0xFF; |
| W_DATA[3] = (reg & 0xFF00) >> 8; |
| s2mm005_write_byte(i2c, S2MM005_REG_I2C_CMD, W_DATA, 4); |
| |
| s2mm005_read_byte(i2c, S2MM005_REG_I2C_RESPONSE, buf, size); |
| } |
| |
| void s2mm005_int_clear(struct s2mm005_data *usbpd_data) |
| { |
| struct i2c_client *i2c = usbpd_data->i2c; |
| u8 val = 0; |
| |
| dev_info(usbpd_data->dev, "%s : -- clear clear -- \n", __func__); |
| val = 0x01; |
| s2mm005_write_byte(i2c, S2MM005_REG_I2C_CMD, &val, 1); |
| } |
| |
| void s2mm005_system_reset(struct s2mm005_data *usbpd_data) |
| { |
| struct i2c_client *i2c = usbpd_data->i2c; |
| u8 R_DATA[2]; |
| u8 W_DATA[2]; |
| |
| s2mm005_sel_read(i2c, 0x1064, R_DATA, SEL_READ_WORD); |
| |
| /* SYSTEM RESET */ |
| W_DATA[0] = R_DATA[0]; |
| W_DATA[1] = R_DATA[1]; |
| |
| s2mm005_sel_write(i2c, 0x1068, W_DATA, SEL_WRITE_WORD); |
| } |