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

View File

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