SPI module finished(?) & compiles. still untested
This commit is contained in:
parent
444d805f0b
commit
dc8e35b389
|
@ -1,5 +1,5 @@
|
|||
|
||||
obj-m := dmj.o dmj-char.o i2c-dmj.o #spi-dmj.c
|
||||
obj-m := dmj.o dmj-char.o i2c-dmj.o spi-dmj.o
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
PWD := $(shell pwd)
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@
|
|||
|
||||
static const uint8_t reqd_cmds[] = {
|
||||
DMJ_SPI_CMD_NOP, DMJ_SPI_CMD_Q_IFACE, DMJ_SPI_CMD_Q_CMDMAP,
|
||||
DMJ_SPI_CMD_Q_WRNMAXLEN, DMJ_SPI_CMD_Q_RDNMAXLEN,
|
||||
DMJ_SPI_CMD_S_SPI_FREQ, DMJ_SPI_CMD_S_PINSTATE, /*DMJ_SPI_CMD_SPIOP,*/
|
||||
/*DMJ_SPI_CMD_Q_WRNMAXLEN, DMJ_SPI_CMD_Q_RDNMAXLEN,*/
|
||||
DMJ_SPI_CMD_S_SPI_FREQ, /*DMJ_SPI_CMD_S_PINSTATE,*/ /*DMJ_SPI_CMD_SPIOP,*/
|
||||
DMJ_SPI_CMD_Q_SPI_CAPS, DMJ_SPI_CMD_S_SPI_CHIPN, DMJ_SPI_CMD_S_SPI_FLAGS,
|
||||
DMJ_SPI_CMD_S_SPI_BPW, DMJ_SPI_CMD_S_SPI_SETCS,
|
||||
/*DMJ_SPI_CMD_SPI_READ, DMJ_SPI_CMD_SPI_WRITE, DMJ_SPI_CMD_SPI_RDWR,*/
|
||||
|
@ -92,6 +92,8 @@ static const uint8_t reqd_cmds[] = {
|
|||
#define DMJ_SPI_S_CAP_CSACHI (1<<9)
|
||||
#define DMJ_SPI_S_CAP_3WIRE (1<<10)
|
||||
|
||||
#define DMJ_PINST_AUTOSUSPEND_TIMEOUT 2000
|
||||
|
||||
struct dmj_spi_caps {
|
||||
uint32_t freq_min, freq_max;
|
||||
uint16_t flgcaps;
|
||||
|
@ -102,21 +104,34 @@ struct dmj_spi_dev_sett {
|
|||
* serializes transfer_one/set_cs calls */
|
||||
uint32_t freq;
|
||||
uint8_t flags, bpw;
|
||||
uint8_t cs;
|
||||
uint8_t cs, pinst;
|
||||
};
|
||||
struct dmj_spi {
|
||||
struct platform_device *pdev;
|
||||
struct spi_controller *spictl;
|
||||
|
||||
uint8_t cmdmap[32];
|
||||
uint8_t *txbuf;
|
||||
struct dmj_spi_caps caps;
|
||||
uint8_t csmask;
|
||||
struct dmj_spi_dev_sett devsettings[8];
|
||||
uint32_t wrnmaxlen, rdnmaxlen;
|
||||
uint8_t cmdmap[32];
|
||||
|
||||
spinlock_t csmap_lock;
|
||||
};
|
||||
|
||||
static int dmj_check_retval_sp(int ret, int len, struct device *dev,
|
||||
const char *pfix, bool check_pos_val, int lmin, int lmax, const void* rbuf)
|
||||
{
|
||||
ret = dmj_check_retval(ret, len, dev, pfix, check_pos_val, lmin, lmax);
|
||||
if (ret >= 0 && ((const uint8_t *)rbuf)[0] != DMJ_SPI_ACK) {
|
||||
dev_err(dev, "%s: did not receive ACK\n", pfix);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool has_cmd(struct dmj_spi *dmjs, int cmd)
|
||||
{
|
||||
int byteind = cmd >> 3, bitind = cmd & 7;
|
||||
|
@ -132,13 +147,13 @@ static uint8_t kernmode_to_flags(uint16_t caps, int mode)
|
|||
else ret |= DMJ_SPI_S_FLG_MSBFST;
|
||||
if (mode & SPI_CS_HIGH) ret |= DMJ_SPI_S_FLG_CSACHI;
|
||||
else ret |= DMJ_SPI_S_FLG_CSACLO;
|
||||
if (mode & SPI_3WIRE) ret |= DMJ_SPI_SL_FLG_3WIRE;
|
||||
if (mode & SPI_3WIRE) ret |= DMJ_SPI_S_FLG_3WIRE;
|
||||
|
||||
/* need some defaults for other stuff */
|
||||
if (caps & DMJ_SPI_S_CAP_STDSPI) ret |= DMJ_SPI_S_FLG_STDSPI;
|
||||
else if (caps & DMJ_SPI_S_CAP_TISSP) ret |= DMJ_SPI_S_FLG_TISSP;
|
||||
else if (caps & DMJ_SPI_S_CAP_MICROW) ret |= DMJ_SPI_S_FLG_MICROW;
|
||||
else ret |= DMJ_SPI_S_FLG_STDSPI; /* shrug */
|
||||
else ret |= DMJ_SPI_S_FLG_STDSPI; /* shrug, also shouldn't happen (cf. get_caps) */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -148,14 +163,14 @@ static int devcaps_to_kernmode(uint16_t caps)
|
|||
|
||||
ret = caps & 3; /* SPI mode (CPHA & CPOL) bits */
|
||||
|
||||
if (caps & DMJ_SPI_S_CAP_LSBFST) ret |= SPI_LB_FIRST;
|
||||
if (caps & DMJ_SPI_S_CAP_LSBFST) ret |= SPI_LSB_FIRST;
|
||||
if (caps & DMJ_SPI_S_CAP_CSACHI) ret |= SPI_CS_HIGH;
|
||||
if (caps & DMJ_SPI_S_CAP_3WIRE) ret |= SPI_3WIRE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bufconv_to_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
|
||||
static void bufconv_to_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
|
||||
{
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
memcpy(dst, src, len_bytes);
|
||||
|
@ -179,7 +194,7 @@ static int bufconv_to_le(void *dst, const void *src, size_t len_bytes, uint8_t b
|
|||
}
|
||||
#endif
|
||||
}
|
||||
static int bufconv_from_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
|
||||
static void bufconv_from_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
|
||||
{
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
memcpy(dst, src, len_bytes);
|
||||
|
@ -206,9 +221,12 @@ static int bufconv_from_le(void *dst, const void *src, size_t len_bytes, uint8_t
|
|||
|
||||
static int dmj_spi_csmask_set(struct dmj_spi *dmjs, uint8_t csmask)
|
||||
{
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t oldcm;
|
||||
int ret;
|
||||
bool do_csmask = false;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_CHIPN, csmask };
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
spin_lock(&dmjs->csmap_lock);
|
||||
oldcm = dmjs->csmask;
|
||||
|
@ -216,11 +234,18 @@ static int dmj_spi_csmask_set(struct dmj_spi *dmjs, uint8_t csmask)
|
|||
dmjs->csmask = csmask;
|
||||
do_csmask = true;
|
||||
}
|
||||
spin_unlock(&dmjs->csmap_unlock);
|
||||
spin_unlock(&dmjs->csmap_lock);
|
||||
|
||||
if (do_csmask) {
|
||||
// TODO: send S_SPI_CHIPN
|
||||
dev_dbg(dev, "set csmask %02x\n", csmask);
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS mask", true, 1, 1, rbuf);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int dmj_spi_csmask_set_one(struct dmj_spi *dmjs, uint8_t cs)
|
||||
{
|
||||
|
@ -228,81 +253,463 @@ static int dmj_spi_csmask_set_one(struct dmj_spi *dmjs, uint8_t cs)
|
|||
}
|
||||
static int dmj_spi_cs_set(struct dmj_spi *dmjs, int ind, bool lvl)
|
||||
{
|
||||
if (dmjs->devsettings[ind].cs == (lvl ? 1 : 0))
|
||||
return 0;
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_SETCS, lvl ? 1 : 0 };
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
// TODO: send S_SPI_SETCS
|
||||
if (dmjs->devsettings[ind].cs == (lvl ? 1 : 0)) return 0;
|
||||
|
||||
dev_dbg(dev, "set cs %s\n", lvl?"hi":"lo");
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS", true, 1, 1, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
dmjs->devsettings[ind].cs = lvl ? 1 : 0;
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_get_caps(struct dmj_spi *dmjs)
|
||||
{
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_Q_SPI_CAPS };
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
dev_dbg(dev, "get caps\n");
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "get caps", true, 14, 14, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
dmjs->caps.freq_min = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
|
||||
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
|
||||
dmjs->caps.freq_max = (uint32_t)rbuf[5] | ((uint32_t)rbuf[6] << 8)
|
||||
| ((uint32_t)rbuf[7] << 16) | ((uint32_t)rbuf[8] << 24);
|
||||
dmjs->caps.flgcaps = (uint32_t)rbuf[9] | ((uint32_t)rbuf[10] << 8);
|
||||
|
||||
dmjs->caps.num_cs = rbuf[11];
|
||||
dmjs->caps.min_bpw = rbuf[12];
|
||||
dmjs->caps.max_bpw = rbuf[13];
|
||||
|
||||
dev_dbg(dev, "got caps: freq=%d..%d, flgcaps=%04hx, bpw=%d..%d, num cs=%d\n",
|
||||
dmjs->caps.freq_min, dmjs->caps.freq_max, dmjs->caps.flgcaps,
|
||||
dmjs->caps.min_bpw, dmjs->caps.max_bpw, dmjs->caps.num_cs);
|
||||
|
||||
if (dmjs->caps.max_bpw == 0 || dmjs->caps.min_bpw == 0) {
|
||||
dev_err(dev, "Device replied with max_bpw=0 or min_bpw=0, wtf?\n");
|
||||
ret = -EXDEV;
|
||||
}
|
||||
if (!(dmjs->caps.flgcaps & (DMJ_SPI_S_CAP_STDSPI
|
||||
| DMJ_SPI_S_CAP_TISSP | DMJ_SPI_S_CAP_MICROW))) {
|
||||
dev_err(dev, "Device does not support any SPI mode, wtf?\n");
|
||||
ret = -EXDEV;
|
||||
}
|
||||
|
||||
kfree(rbuf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_get_caps(struct dmj_spi *dmjs); // TODO: check if bpw_min > 0
|
||||
static int dmj_spi_set_freq(struct dmj_spi *dmjs, int ind, uint32_t freq)
|
||||
{
|
||||
if (dmjs->devsettings[ind].freq == freq)
|
||||
return 0;
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_FREQ,
|
||||
freq, freq >> 8, freq >> 16, freq >> 24 };
|
||||
uint8_t *rbuf;
|
||||
uint32_t freqret;
|
||||
int ret, rlen;
|
||||
|
||||
// TODO: send S_SPI_FREQ
|
||||
if (dmjs->devsettings[ind].freq == freq) return 0;
|
||||
|
||||
dev_dbg(dev, "set freq to %u\n", freq);
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS", true, 5, 5, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
freqret = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
|
||||
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
|
||||
|
||||
if (freqret != freq) {
|
||||
dev_warn(dev, "set frequency: couldn't provide exact freq %u Hz, %u Hz was applied instead.\n",
|
||||
freq, freqret);
|
||||
}
|
||||
|
||||
/* not the returned one, to avoid resending */
|
||||
dmjs->devsettings[ind].freq = freq;
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_set_flags(struct dmj_spi *dmjs, int ind, uint8_t flags)
|
||||
{
|
||||
if (dmjs->devsettings[ind].flags == flags)
|
||||
return 0;
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_FLAGS, flags };
|
||||
uint8_t *rbuf, flagret;
|
||||
int ret, rlen;
|
||||
|
||||
// TODO: send S_SPI_FLAGS
|
||||
if (dmjs->devsettings[ind].flags == flags) return 0;
|
||||
|
||||
dev_dbg(dev, "set flags %08x\n", flags);
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set flags", true, 2, 2, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
flagret = rbuf[1];
|
||||
|
||||
if (flagret != flags) {
|
||||
dev_warn(dev, "set flags: couldn't set exact flags %08x, was set to %08x instead\n",
|
||||
flags, flagret);
|
||||
}
|
||||
|
||||
/* not the returned one, to avoid resending */
|
||||
dmjs->devsettings[ind].flags = flags;
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_set_bpw(struct dmj_spi *dmjs, int ind, uint8_t bpw)
|
||||
{
|
||||
if (dmjs->devsettings[ind].bpw == bpw)
|
||||
return 0;
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_BPW, bpw };
|
||||
uint8_t *rbuf, bpwret;
|
||||
int ret, rlen;
|
||||
|
||||
if (dmjs->devsettings[ind].bpw == bpw) return 0;
|
||||
|
||||
// TODO: send S_SPI_BPW
|
||||
dev_dbg(dev, "set bpw %hhu\n", bpw);
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set bpw", true, 2, 2, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
bpwret = rbuf[1];
|
||||
|
||||
if (bpwret != bpw) {
|
||||
dev_warn(dev, "set flags: couldn't set exact bpw %hhu, was set to %hhu instead\n",
|
||||
bpw, bpwret);
|
||||
}
|
||||
|
||||
/* not the returned one, to avoid resending */
|
||||
dmjs->devsettings[ind].bpw = bpw;
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_set_pinstate(struct dmj_spi *dmjs, bool pins)
|
||||
{
|
||||
if (!has_cmd(dmjs, DMJ_SPI_CMD_S_PINSTATE))
|
||||
return 0;
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_S_PINSTATE, pins ? 1 : 0 };
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
// TODO: do cmd
|
||||
if (!has_cmd(dmjs, DMJ_SPI_CMD_S_PINSTATE)) return 0;
|
||||
|
||||
dev_dbg(dev, "set pinstate %sabled\n", pins?"en":"dis");
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "set pinstate", true, 2, 2, rbuf);
|
||||
|
||||
/*if (!ret) {
|
||||
dmjs->devsettings[ind].pinst = pins;
|
||||
}*/
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dmj_spi_check_hw(struct dmj_spi *dmjs)
|
||||
{
|
||||
/*
|
||||
* TODO: check:
|
||||
* * NOP, SYNCNOP
|
||||
* * Q_IFACE retval (must be SERPROG_IFACE_VERSION)
|
||||
* * ^ + SPIOP -or- READ & WRITE support
|
||||
* * Q_CMDMAP (cf. reqd_cmds)
|
||||
* * RDNMAXLEN, WRNMAXLEN
|
||||
*/
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t wbuf[] = { DMJ_SPI_CMD_NOP };
|
||||
uint8_t *rbuf;
|
||||
uint16_t iface;
|
||||
int ret, rlen, i;
|
||||
|
||||
dev_dbg(dev, "check hw: nop");
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: nop", true, 1, 1, rbuf);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
|
||||
dev_dbg(dev, "check hw: syncnop");
|
||||
wbuf[0] = DMJ_SPI_CMD_SYNCNOP;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval(ret, rlen, dev, "check hw: nop", true, 2, 2);
|
||||
if (!ret) {
|
||||
if (rbuf[0] != DMJ_SPI_NAK || rbuf[1] != DMJ_SPI_ACK) {
|
||||
dev_err(dev, "check hw: syncnop: bad response %02x %02x\n",
|
||||
rbuf[0], rbuf[1]);
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
|
||||
dev_dbg(dev, "check hw: iface");
|
||||
wbuf[0] = DMJ_SPI_CMD_Q_IFACE;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: iface", true, 3, 3, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
iface = (uint16_t)rbuf[1] | ((uint16_t)rbuf[2] << 8);
|
||||
|
||||
if (iface != SERPROG_IFACE_VERSION) {
|
||||
dev_err(dev, "check hw: iface: bad serprog version: expected %hu, got %hu\n",
|
||||
SERPROG_IFACE_VERSION, iface);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
|
||||
dev_dbg(dev, "check hw: cmdmap");
|
||||
wbuf[0] = DMJ_SPI_CMD_Q_CMDMAP;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: cmdmap", true, 33, 33, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
memcpy(dmjs->cmdmap, &rbuf[1], 32);
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
|
||||
for (i = 0; i < sizeof(reqd_cmds)/sizeof(*reqd_cmds); ++i) {
|
||||
if (!has_cmd(dmjs, reqd_cmds[i])) {
|
||||
dev_err(dev, "device does not have required serprog command %02x\n", reqd_cmds[i]);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static int dmj_spi_do_read(struct dmj_spi *dmjs, void *data, size_t len)
|
||||
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_PGMNAME)) {
|
||||
dev_dbg(dev, "check hw: pgmname");
|
||||
wbuf[0] = DMJ_SPI_CMD_Q_PGMNAME;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: pgmname", true, 17, 17, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
rbuf[16] = 0;
|
||||
dev_info(dev, "Serprog pgmname: '%s'\n", rbuf);
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_WRNMAXLEN)) {
|
||||
dev_dbg(dev, "check hw: wrnmaxlen");
|
||||
wbuf[0] = DMJ_SPI_CMD_Q_WRNMAXLEN;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: wrnmaxlen", true, 4, 4, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
dmjs->wrnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
} else dmjs->rdnmaxlen = 512;
|
||||
dev_info(dev, " wrnmaxlen = 0x%x\n", dmjs->wrnmaxlen);
|
||||
|
||||
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_RDNMAXLEN)) {
|
||||
dev_dbg(dev, "check hw: rdnmaxlen");
|
||||
wbuf[0] = DMJ_SPI_CMD_Q_RDNMAXLEN;
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: rdnmaxlen", true, 4, 4, rbuf);
|
||||
|
||||
if (!ret) {
|
||||
dmjs->rdnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
|
||||
}
|
||||
if (rbuf) kfree(rbuf);
|
||||
if (ret) return ret;
|
||||
} else dmjs->rdnmaxlen = 512;
|
||||
dev_info(dev, " rdnmaxlen = 0x%x\n", dmjs->rdnmaxlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmj_spi_do_read(struct dmj_spi *dmjs, void *data, size_t len, uint8_t bpw)
|
||||
{
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
if (len > INT_MAX-4) return -EINVAL;
|
||||
|
||||
if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_READ)) {
|
||||
/* TODO: do SPI_READ */
|
||||
dev_dbg(dev, "do spi read len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_READ;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, 4, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do read", true, (int)len+1, (int)len+1, rbuf);
|
||||
|
||||
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPIOP)) {
|
||||
dev_dbg(dev, "do spiop read len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPIOP;
|
||||
dmjs->txbuf[1] = 0;
|
||||
dmjs->txbuf[2] = 0;
|
||||
dmjs->txbuf[3] = 0;
|
||||
dmjs->txbuf[4] = len & 0xff;
|
||||
dmjs->txbuf[5] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[6] = (len >> 16) & 0xff;
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, 7, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do spiop read", true, (int)len+1, (int)len+1, rbuf);
|
||||
|
||||
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) {
|
||||
dev_dbg(dev, "do rdwr read len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
|
||||
/* we need to fill the buffer with stuff bits to control the data
|
||||
* that will get sent over the full duplex line. 0 is the default used
|
||||
* in most places apparently? */
|
||||
memset(&dmjs->txbuf[4], 0, len);
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr read", true, (int)len+1, (int)len+1, rbuf);
|
||||
|
||||
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else {
|
||||
/* TODO: do SPIOP */
|
||||
return -EXDEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int dmj_spi_do_write(struct dmj_spi *dmjs, const void *data, size_t len)
|
||||
static int dmj_spi_do_write(struct dmj_spi *dmjs, const void *data, size_t len, uint8_t bpw)
|
||||
{
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
if (len > INT_MAX-7) return -EINVAL;
|
||||
|
||||
if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_WRITE)) {
|
||||
/* TODO: do SPI_WRITE */
|
||||
dev_dbg(dev, "do spi write len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_WRITE;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
|
||||
bufconv_to_le(&dmjs->txbuf[4], data, len, bpw);
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do write", true, 1, 1, rbuf);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPIOP)) {
|
||||
dev_dbg(dev, "do spiop write len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPIOP;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
dmjs->txbuf[4] = 0;
|
||||
dmjs->txbuf[5] = 0;
|
||||
dmjs->txbuf[6] = 0;
|
||||
|
||||
bufconv_to_le(&dmjs->txbuf[7], data, len, bpw);
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, (int)len+7, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do spiop write", true, 1, 1, rbuf);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) {
|
||||
dev_dbg(dev, "do rdwr write len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
|
||||
bufconv_to_le(&dmjs->txbuf[4], data, len, bpw);
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr write", true, (int)len+1, (int)len+1, rbuf);
|
||||
/* we just don't look at the returned bytes in this case */
|
||||
if (rbuf) kfree(rbuf);
|
||||
} else {
|
||||
/* TODO: do SPIOP */
|
||||
return -EXDEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* should only be called if it already has the cmd anyway (cf. spi_controller->flags) */
|
||||
static int dmj_spi_do_rdwr(struct dmj_spi *dmjs, void *rdata, const void *wdata, size_t len);
|
||||
static int dmj_spi_do_rdwr(struct dmj_spi *dmjs, void *rdata, const void *wdata, size_t len, uint8_t bpw)
|
||||
{
|
||||
struct device *dev = &dmjs->pdev->dev;
|
||||
uint8_t *rbuf;
|
||||
int ret, rlen;
|
||||
|
||||
if (len > INT_MAX-4) return -EINVAL;
|
||||
if (!has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) return -EXDEV;
|
||||
|
||||
dev_dbg(dev, "do rdwr len=0x%zx\n", len);
|
||||
|
||||
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
|
||||
dmjs->txbuf[1] = len & 0xff;
|
||||
dmjs->txbuf[2] = (len >> 8) & 0xff;
|
||||
dmjs->txbuf[3] = (len >> 16) & 0xff;
|
||||
|
||||
bufconv_to_le(&dmjs->txbuf[4], wdata, len, bpw);
|
||||
|
||||
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
|
||||
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
|
||||
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr", true, (int)len+1, -1, rbuf);
|
||||
|
||||
if (!ret) bufconv_from_le(rdata, &rbuf[1], len, bpw);
|
||||
|
||||
if (rbuf) kfree(rbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dmj_spi_prepare_message(struct spi_controller *spictl, struct spi_message *msg)
|
||||
{
|
||||
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
|
||||
struct spi_device *spidev = message->spi;
|
||||
struct spi_device *spidev = msg->spi;
|
||||
struct device *dev = &spidev->dev;
|
||||
int ret;
|
||||
|
||||
ret = dmj_spi_set_flags(dmjs, spidev->chip_select, kernmode_to_flags(spidev->mode));
|
||||
ret = dmj_spi_set_flags(dmjs, spidev->chip_select,
|
||||
kernmode_to_flags(dmjs->caps.flgcaps, spidev->mode));
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set SPI flags\n");
|
||||
return ret;
|
||||
|
@ -316,26 +723,26 @@ static int dmj_spi_prepare_message(struct spi_controller *spictl, struct spi_mes
|
|||
|
||||
return ret;
|
||||
}
|
||||
static int dmj_spi_set_cs(struct spi_device *spidev, bool enable)
|
||||
static void dmj_spi_set_cs(struct spi_device *spidev, bool enable)
|
||||
{
|
||||
struct spi_controller *spictl = spidev->controller;
|
||||
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
|
||||
struct spi_device *spidev = message->spi;
|
||||
struct device *dev = &spidev->dev;
|
||||
int ret;
|
||||
|
||||
ret = dmj_spi_csmask_set_one(dmjs, spidev->chip_select);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set CS mask\n");
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = dmj_spi_cs_set(dmjs, spidev->chip_select, enable);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set chip select line\n");
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/*return 0;*/
|
||||
}
|
||||
static int dmj_spi_transfer_one(struct spi_controller *spictl, struct spi_device *spidev, struct spi_transfer *xfer)
|
||||
{
|
||||
|
@ -350,13 +757,12 @@ static int dmj_spi_transfer_one(struct spi_controller *spictl, struct spi_device
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = dmj_spi_set_bpw(dmjs, spidev->chip_select, xfer->bpw);
|
||||
ret = dmj_spi_set_bpw(dmjs, spidev->chip_select, xfer->bits_per_word);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set SPI bits-per-word to %d\n", xfer->bits_per_word);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cksize;
|
||||
if (xfer->tx_buf && xfer->rx_buf) {
|
||||
cksize = dmjs->wrnmaxlen;
|
||||
if (cksize > dmjs->rdnmaxlen) cksize = dmjs->rdnmaxlen;
|
||||
|
@ -371,14 +777,17 @@ static int dmj_spi_transfer_one(struct spi_controller *spictl, struct spi_device
|
|||
if (todo < cksize) cksize = todo;
|
||||
|
||||
if (xfer->tx_buf && xfer->rx_buf) {
|
||||
ret = dmj_spi_do_rdwr(dmjs, xfer->rx_buf + off, xfer->tx_buf + off, cksize);
|
||||
ret = dmj_spi_do_rdwr(dmjs, xfer->rx_buf + off, xfer->tx_buf + off, cksize, xfer->bits_per_word);
|
||||
} else if (xfer->tx_buf) {
|
||||
ret = dmj_spi_do_write(dmjs, xfer->tx_buf + off, cksize);
|
||||
ret = dmj_spi_do_write(dmjs, xfer->tx_buf + off, cksize, xfer->bits_per_word);
|
||||
} else /*if (xfer->rx_buf)*/ {
|
||||
ret = dmj_spi_do_read(dmjs, xfer->rx_Buf + off, cksize);
|
||||
ret = dmj_spi_do_read(dmjs, xfer->rx_buf + off, cksize, xfer->bits_per_word);
|
||||
}
|
||||
|
||||
if (ret < 0) return ret;
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "SPI transfer failed! %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
todo -= cksize;
|
||||
off += cksize;
|
||||
|
@ -392,12 +801,12 @@ static int dmj_spi_probe(struct platform_device *pdev)
|
|||
struct spi_controller *spictl;
|
||||
struct dmj_spi *dmjs;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
controller = spi_alloc_controller(dev, sizeof(*dmjs));
|
||||
if (!controller) return -ENOMEM;
|
||||
spictl = spi_alloc_master(dev, sizeof(*dmjs));
|
||||
if (!spictl ) return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, controller);
|
||||
platform_set_drvdata(pdev, spictl);
|
||||
|
||||
dmjs = spi_controller_get_devdata(spictl);
|
||||
|
||||
|
@ -419,6 +828,13 @@ static int dmj_spi_probe(struct platform_device *pdev)
|
|||
goto err_free_ctl;
|
||||
}
|
||||
|
||||
dmjs->txbuf = devm_kmalloc(&pdev->dev, dmjs->wrnmaxlen + 0x10, GFP_KERNEL);
|
||||
if (!dmjs->txbuf) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "No memory left for TX buffer of length 0x%x\n", dmjs->wrnmaxlen);
|
||||
goto err_free_ctl;
|
||||
}
|
||||
|
||||
ret = dmj_spi_get_caps(dmjs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to get device capabilities\n");
|
||||
|
@ -440,10 +856,10 @@ static int dmj_spi_probe(struct platform_device *pdev)
|
|||
if (!has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR))
|
||||
spictl->flags |= SPI_CONTROLLER_HALF_DUPLEX;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev, DMJ_RPM_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_set_autosuspend_delay(dev, DMJ_PINST_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_enable(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = devm_spi_register_controller(dev, spictl);
|
||||
if (ret < 0) {
|
||||
|
|
Loading…
Reference in New Issue