fix I2C driver (and transfer routine bugs)
This commit is contained in:
parent
a8ad3f6e04
commit
bad06fe9b4
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue