fix I2C driver (and transfer routine bugs)

This commit is contained in:
Triss 2021-07-14 23:26:04 +02:00
parent a8ad3f6e04
commit bad06fe9b4
4 changed files with 41 additions and 55 deletions

View File

@ -7,8 +7,11 @@
*/
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/device.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) /* TODO: make this check more precise */
#include <linux/device/class.h>
#endif
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>

View File

@ -131,7 +131,7 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
* value when passed to the function will not matter
*/
if (*rbufsize) *rbufsize = -1;
if (rbufsize) *rbufsize = -1;
tmpbuf = kmalloc(64, GFP_KERNEL);
if (!tmpbuf) return -ENOMEM;
@ -287,11 +287,9 @@ err_freetmp:
int dmj_transfer(struct platform_device *pdev, int cmd, int recvflags,
const void *wbuf, int wbufsize, void **rbuf, int *rbufsize)
{
struct dmj_platform_data *dmj_pdata;
struct dmj_dev *dmj;
dmj = dev_get_drvdata(pdev->dev.parent);
dmj_pdata = dev_get_platdata(&pdev->dev); /* TODO: ??? */
return dmj_xfer_internal(dmj, cmd, recvflags, wbuf, wbufsize, rbuf, rbufsize);
}
@ -333,16 +331,16 @@ static int dmj_print_info(struct dmj_dev *dmj)
uint8_t curmode, features;
struct device *dev = &dmj->interface->dev;
uint8_t *buf;
char modeinfo[16];
char *strinfo;
char modeinfo[16], namebuf[64];
/* info string */
ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_INFOSTR,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, sizeof(strinfo));
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !buf) goto out;
buf[len] = 0;
dev_info(dev, HARDWARE_NAME " '%s'\n", buf);
memcpy(namebuf, buf, len);
namebuf[len] = 0;
dev_info(dev, HARDWARE_NAME " '%s'\n", namebuf);
kfree(buf); buf = NULL;
/* cur mode */
@ -377,16 +375,17 @@ static int dmj_print_info(struct dmj_dev *dmj)
/* name */
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_NAME,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, -1);
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !buf) goto out;
buf[len] = 0;
strinfo = buf; buf = NULL;
memcpy(namebuf, buf, len);
namebuf[len] = 0;
kfree(buf); buf = NULL;
/* version */
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(mversion), sizeof(mversion));
if (ret < 0 || !buf) { kfree(strinfo); goto out; }
if (ret < 0 || !buf) goto out;
mversion = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
@ -394,7 +393,7 @@ static int dmj_print_info(struct dmj_dev *dmj)
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_FEATURES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(features), sizeof(features));
if (ret < 0 || !buf) { kfree(strinfo); goto out; }
if (ret < 0 || !buf) goto out;
features = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
@ -407,8 +406,7 @@ static int dmj_print_info(struct dmj_dev *dmj)
modeinfo[8] = 0;
dev_dbg(dev, "Mode %d: '%s' version 0x%04x, features: %s\n",
i, strinfo, mversion, modeinfo);
kfree(strinfo);
i, namebuf, mversion, modeinfo);
}
return 0;

View File

@ -30,10 +30,6 @@
#define DMJ_FEATURE_MODE1_I2C (1<<3)
#define DMJ_FEATURE_MODE1_TEMPSENSOR (1<<4)
struct dmj_platform_data {
uint8_t port;
};
#define DMJ_XFER_FLAGS_PARSE_RESP (1<<0)
#define DMJ_XFER_FLAGS_FILL_RECVBUF (1<<1)

View File

@ -123,7 +123,7 @@ static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
pmsg = &msgs[i];
dev_warn(&a->dev,
dev_dbg(&a->dev,
" %d: %s (flags %04x) %d bytes to 0x%02x\n",
i, pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->flags, pmsg->len, pmsg->addr);
@ -150,10 +150,10 @@ static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
i2ccmd = DMJ_I2C_CMD_GET_STATUS;
ret = dmj_transfer(dmji->pdev, DMJ_CMD_MODE1_I2C, DMJ_XFER_FLAGS_PARSE_RESP,
&i2ccmd, sizeof(i2ccmd), (void**)&status, &stlen);
ret = dmj_check_retval(ret, stlen, dev, "i2c stat", true, sizeof(status), sizeof(status));
ret = dmj_check_retval(ret, stlen, dev, "i2c stat", true, sizeof(*status), sizeof(*status));
if (ret < 0 || !status) goto err_ret;
dev_warn(dev, " status = %d\n", *status);
dev_dbg(&a->dev, " status = %d\n", *status);
if (*status == DMJ_I2C_STAT_NAK) {
ret = -ENXIO;
goto err_ret;
@ -169,7 +169,7 @@ err_ret:
static uint32_t dmj_i2c_func(struct i2c_adapter *a)
{
struct dmj_i2c *dmji = i2c_get_adapdata(a);
struct device *dev = &dmji->pdev->dev;
struct device *dev = /*&dmji->pdev->dev;*/ &a->dev;
uint32_t func = 0;
int len, ret;
@ -184,7 +184,7 @@ static uint32_t dmj_i2c_func(struct i2c_adapter *a)
func = (uint32_t)fbuf[0] | ((uint32_t)fbuf[1] << 8)
| ((uint32_t)fbuf[2] << 16) | ((uint32_t)fbuf[3] << 24);
dev_warn(dev, "I2C functionality: 0x%08x\n", func);
dev_dbg(dev, "I2C functionality: 0x%08x\n", func);
kfree(fbuf);
return func;
@ -221,8 +221,6 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
ret = dmj_check_retval(ret, len, dev, "i2c test 1", true, sizeof(curmode), sizeof(curmode));
if (ret < 0 || !buf) goto out;
dev_warn(dev, "check hw 1\n");
curmode = buf[0];
kfree(buf); buf = NULL;
if (curmode != 0x1) {
@ -231,15 +229,11 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
dev_warn(dev, "check hw 2\n");
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test 2", true, sizeof(m1ver), sizeof(m1ver));
if (ret < 0 || !buf) goto out;
dev_warn(dev, "check hw 3\n");
m1ver = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
if (m1ver > ver_max || m1ver < ver_min) {
@ -249,8 +243,6 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
dev_warn(dev, "check hw 4\n");
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_FEATURES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test 3", true, sizeof(m1feat), sizeof(m1feat));
@ -263,8 +255,6 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
dev_warn(dev, "check hw 5\n");
echoval = 0x42;
i2ccmd[0] = DMJ_I2C_CMD_ECHO;
i2ccmd[1] = ~echoval;
@ -281,13 +271,9 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
dev_warn(dev, "check hw 6\n");
ret = 0;
out:
dev_warn(dev, "check hw 7\n");
if (buf) kfree(buf);
return ret;
}
@ -298,8 +284,6 @@ static int dmj_i2c_set_delay(struct platform_device *pdev, uint16_t us)
uint8_t i2ccmd[3];
int ret = 0;
dev_warn(dev, "set delay 1\n");
i2ccmd[0] = DMJ_I2C_CMD_SET_DELAY;
i2ccmd[1] = (us >> 0) & 0xff;
i2ccmd[2] = (us >> 8) & 0xff;
@ -308,18 +292,16 @@ static int dmj_i2c_set_delay(struct platform_device *pdev, uint16_t us)
dev_dbg(dev, "set delay to %hu us, result %d\n", us, ret);
ret = dmj_check_retval(ret, -1, dev, "i2c set delay", true, -1, -1);
dev_warn(dev, "set delay 2\n");
return ret;
}
static int dmj_i2c_probe(struct platform_device *pdev)
{
int ret;
int ret, hwnlen;
struct dmj_i2c *dmji;
struct device *dev = &pdev->dev;
dev_warn(dev, "i2c probe hi!\n");
void *hwname;
char namebuf[64];
ret = dmj_i2c_check_hw(pdev);
if (ret) return -ENODEV;
@ -335,28 +317,35 @@ static int dmj_i2c_probe(struct platform_device *pdev)
dmji->pdev = pdev;
dev_warn(dev, "probe 2\n");
dmji->adapter.owner = THIS_MODULE;
dmji->adapter.class = I2C_CLASS_HWMON;
dmji->adapter.algo = &dmj_i2c_algo;
dmji->adapter.quirks = &dmj_i2c_quirks; /* TODO: is this needed? probably... */
dmji->adapter.dev.of_node = dev->of_node;
dev_warn(dev, "probe 3\n");
i2c_set_adapdata(&dmji->adapter, dmji);
dev_warn(dev, "probe 4\n");
/* get device name, for adapter name */
ret = dmj_transfer(pdev, DMJ_CMD_CFG_GET_INFOSTR,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&hwname, &hwnlen);
ret = dmj_check_retval(ret, hwnlen, dev, "probe: get name", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !hwname) return -EIO;
memcpy(namebuf, hwname, hwnlen);
namebuf[hwnlen] = 0;
kfree(hwname);
snprintf(dmji->adapter.name, sizeof(dmji->adapter.name), "%s-%s",
"dmj-i2c", dev_name(pdev->dev.parent));
dev_warn(dev, "probe 5\n");
snprintf(dmji->adapter.name, sizeof(dmji->adapter.name),
HARDWARE_NAME " '%s' at %s", namebuf, dev_name(pdev->dev.parent));
platform_set_drvdata(pdev, dmji);
dev_warn(dev, "probe 6\n");
ret = i2c_add_adapter(&dmji->adapter);
return i2c_add_adapter(&dmji->adapter);
if (!ret) {
dev_info(dev, HARDWARE_NAME " I2C device driver at i2c-%d, %s\n",
dmji->adapter.nr, dmji->adapter.name);
}
return ret;
}
static int dmj_i2c_remove(struct platform_device *pdev)
{