162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Nano River Technologies viperboard i2c master driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  (C) 2012 by Lemonage GmbH
662306a36Sopenharmony_ci *  Author: Lars Poeschel <poeschel@lemonage.de>
762306a36Sopenharmony_ci *  All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/usb.h>
1962306a36Sopenharmony_ci#include <linux/i2c.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/mfd/viperboard.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct vprbrd_i2c {
2462306a36Sopenharmony_ci	struct i2c_adapter i2c;
2562306a36Sopenharmony_ci	u8 bus_freq_param;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* i2c bus frequency module parameter */
2962306a36Sopenharmony_cistatic u8 i2c_bus_param;
3062306a36Sopenharmony_cistatic unsigned int i2c_bus_freq = 100;
3162306a36Sopenharmony_cimodule_param(i2c_bus_freq, int, 0);
3262306a36Sopenharmony_ciMODULE_PARM_DESC(i2c_bus_freq,
3362306a36Sopenharmony_ci	"i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int vprbrd_i2c_status(struct i2c_adapter *i2c,
3662306a36Sopenharmony_ci	struct vprbrd_i2c_status *status, bool prev_error)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	u16 bytes_xfer;
3962306a36Sopenharmony_ci	int ret;
4062306a36Sopenharmony_ci	struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* check for protocol error */
4362306a36Sopenharmony_ci	bytes_xfer = sizeof(struct vprbrd_i2c_status);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
4662306a36Sopenharmony_ci		VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000,
4762306a36Sopenharmony_ci		status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (ret != bytes_xfer)
5062306a36Sopenharmony_ci		prev_error = true;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (prev_error) {
5362306a36Sopenharmony_ci		dev_err(&i2c->dev, "failure in usb communication\n");
5462306a36Sopenharmony_ci		return -EREMOTEIO;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	dev_dbg(&i2c->dev, "  status = %d\n", status->status);
5862306a36Sopenharmony_ci	if (status->status != 0x00) {
5962306a36Sopenharmony_ci		dev_err(&i2c->dev, "failure: i2c protocol error\n");
6062306a36Sopenharmony_ci		return -EPROTO;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	return 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int vprbrd_i2c_receive(struct usb_device *usb_dev,
6662306a36Sopenharmony_ci	struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int ret, bytes_actual;
6962306a36Sopenharmony_ci	int error = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* send the read request */
7262306a36Sopenharmony_ci	ret = usb_bulk_msg(usb_dev,
7362306a36Sopenharmony_ci		usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg,
7462306a36Sopenharmony_ci		sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual,
7562306a36Sopenharmony_ci		VPRBRD_USB_TIMEOUT_MS);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if ((ret < 0)
7862306a36Sopenharmony_ci		|| (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) {
7962306a36Sopenharmony_ci		dev_err(&usb_dev->dev, "failure transmitting usb\n");
8062306a36Sopenharmony_ci		error = -EREMOTEIO;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* read the actual data */
8462306a36Sopenharmony_ci	ret = usb_bulk_msg(usb_dev,
8562306a36Sopenharmony_ci		usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg,
8662306a36Sopenharmony_ci		bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if ((ret < 0) || (bytes_xfer != bytes_actual)) {
8962306a36Sopenharmony_ci		dev_err(&usb_dev->dev, "failure receiving usb\n");
9062306a36Sopenharmony_ci		error = -EREMOTEIO;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	return error;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int vprbrd_i2c_addr(struct usb_device *usb_dev,
9662306a36Sopenharmony_ci	struct vprbrd_i2c_addr_msg *amsg)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	int ret, bytes_actual;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	ret = usb_bulk_msg(usb_dev,
10162306a36Sopenharmony_ci		usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg,
10262306a36Sopenharmony_ci		sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual,
10362306a36Sopenharmony_ci		VPRBRD_USB_TIMEOUT_MS);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if ((ret < 0) ||
10662306a36Sopenharmony_ci			(sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) {
10762306a36Sopenharmony_ci		dev_err(&usb_dev->dev, "failure transmitting usb\n");
10862306a36Sopenharmony_ci		return -EREMOTEIO;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int ret;
11662306a36Sopenharmony_ci	u16 remain_len, len1, len2, start = 0x0000;
11762306a36Sopenharmony_ci	struct vprbrd_i2c_read_msg *rmsg =
11862306a36Sopenharmony_ci		(struct vprbrd_i2c_read_msg *)vb->buf;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	remain_len = msg->len;
12162306a36Sopenharmony_ci	rmsg->header.cmd = VPRBRD_I2C_CMD_READ;
12262306a36Sopenharmony_ci	while (remain_len > 0) {
12362306a36Sopenharmony_ci		rmsg->header.addr = cpu_to_le16(start + 0x4000);
12462306a36Sopenharmony_ci		if (remain_len <= 255) {
12562306a36Sopenharmony_ci			len1 = remain_len;
12662306a36Sopenharmony_ci			len2 = 0x00;
12762306a36Sopenharmony_ci			rmsg->header.len0 = remain_len;
12862306a36Sopenharmony_ci			rmsg->header.len1 = 0x00;
12962306a36Sopenharmony_ci			rmsg->header.len2 = 0x00;
13062306a36Sopenharmony_ci			rmsg->header.len3 = 0x00;
13162306a36Sopenharmony_ci			rmsg->header.len4 = 0x00;
13262306a36Sopenharmony_ci			rmsg->header.len5 = 0x00;
13362306a36Sopenharmony_ci			remain_len = 0;
13462306a36Sopenharmony_ci		} else if (remain_len <= 510) {
13562306a36Sopenharmony_ci			len1 = remain_len;
13662306a36Sopenharmony_ci			len2 = 0x00;
13762306a36Sopenharmony_ci			rmsg->header.len0 = remain_len - 255;
13862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
13962306a36Sopenharmony_ci			rmsg->header.len2 = 0x00;
14062306a36Sopenharmony_ci			rmsg->header.len3 = 0x00;
14162306a36Sopenharmony_ci			rmsg->header.len4 = 0x00;
14262306a36Sopenharmony_ci			rmsg->header.len5 = 0x00;
14362306a36Sopenharmony_ci			remain_len = 0;
14462306a36Sopenharmony_ci		} else if (remain_len <= 512) {
14562306a36Sopenharmony_ci			len1 = remain_len;
14662306a36Sopenharmony_ci			len2 = 0x00;
14762306a36Sopenharmony_ci			rmsg->header.len0 = remain_len - 510;
14862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
14962306a36Sopenharmony_ci			rmsg->header.len2 = 0xff;
15062306a36Sopenharmony_ci			rmsg->header.len3 = 0x00;
15162306a36Sopenharmony_ci			rmsg->header.len4 = 0x00;
15262306a36Sopenharmony_ci			rmsg->header.len5 = 0x00;
15362306a36Sopenharmony_ci			remain_len = 0;
15462306a36Sopenharmony_ci		} else if (remain_len <= 767) {
15562306a36Sopenharmony_ci			len1 = 512;
15662306a36Sopenharmony_ci			len2 = remain_len - 512;
15762306a36Sopenharmony_ci			rmsg->header.len0 = 0x02;
15862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
15962306a36Sopenharmony_ci			rmsg->header.len2 = 0xff;
16062306a36Sopenharmony_ci			rmsg->header.len3 = remain_len - 512;
16162306a36Sopenharmony_ci			rmsg->header.len4 = 0x00;
16262306a36Sopenharmony_ci			rmsg->header.len5 = 0x00;
16362306a36Sopenharmony_ci			remain_len = 0;
16462306a36Sopenharmony_ci		} else if (remain_len <= 1022) {
16562306a36Sopenharmony_ci			len1 = 512;
16662306a36Sopenharmony_ci			len2 = remain_len - 512;
16762306a36Sopenharmony_ci			rmsg->header.len0 = 0x02;
16862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
16962306a36Sopenharmony_ci			rmsg->header.len2 = 0xff;
17062306a36Sopenharmony_ci			rmsg->header.len3 = remain_len - 767;
17162306a36Sopenharmony_ci			rmsg->header.len4 = 0xff;
17262306a36Sopenharmony_ci			rmsg->header.len5 = 0x00;
17362306a36Sopenharmony_ci			remain_len = 0;
17462306a36Sopenharmony_ci		} else if (remain_len <= 1024) {
17562306a36Sopenharmony_ci			len1 = 512;
17662306a36Sopenharmony_ci			len2 = remain_len - 512;
17762306a36Sopenharmony_ci			rmsg->header.len0 = 0x02;
17862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
17962306a36Sopenharmony_ci			rmsg->header.len2 = 0xff;
18062306a36Sopenharmony_ci			rmsg->header.len3 = remain_len - 1022;
18162306a36Sopenharmony_ci			rmsg->header.len4 = 0xff;
18262306a36Sopenharmony_ci			rmsg->header.len5 = 0xff;
18362306a36Sopenharmony_ci			remain_len = 0;
18462306a36Sopenharmony_ci		} else {
18562306a36Sopenharmony_ci			len1 = 512;
18662306a36Sopenharmony_ci			len2 = 512;
18762306a36Sopenharmony_ci			rmsg->header.len0 = 0x02;
18862306a36Sopenharmony_ci			rmsg->header.len1 = 0xff;
18962306a36Sopenharmony_ci			rmsg->header.len2 = 0xff;
19062306a36Sopenharmony_ci			rmsg->header.len3 = 0x02;
19162306a36Sopenharmony_ci			rmsg->header.len4 = 0xff;
19262306a36Sopenharmony_ci			rmsg->header.len5 = 0xff;
19362306a36Sopenharmony_ci			remain_len -= 1024;
19462306a36Sopenharmony_ci			start += 1024;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci		rmsg->header.tf1 = cpu_to_le16(len1);
19762306a36Sopenharmony_ci		rmsg->header.tf2 = cpu_to_le16(len2);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		/* first read transfer */
20062306a36Sopenharmony_ci		ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1);
20162306a36Sopenharmony_ci		if (ret < 0)
20262306a36Sopenharmony_ci			return ret;
20362306a36Sopenharmony_ci		/* copy the received data */
20462306a36Sopenharmony_ci		memcpy(msg->buf + start, rmsg, len1);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		/* second read transfer if neccessary */
20762306a36Sopenharmony_ci		if (len2 > 0) {
20862306a36Sopenharmony_ci			ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2);
20962306a36Sopenharmony_ci			if (ret < 0)
21062306a36Sopenharmony_ci				return ret;
21162306a36Sopenharmony_ci			/* copy the received data */
21262306a36Sopenharmony_ci			memcpy(msg->buf + start + 512, rmsg, len2);
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	int ret, bytes_actual;
22162306a36Sopenharmony_ci	u16 remain_len, bytes_xfer,
22262306a36Sopenharmony_ci		start = 0x0000;
22362306a36Sopenharmony_ci	struct vprbrd_i2c_write_msg *wmsg =
22462306a36Sopenharmony_ci		(struct vprbrd_i2c_write_msg *)vb->buf;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	remain_len = msg->len;
22762306a36Sopenharmony_ci	wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE;
22862306a36Sopenharmony_ci	wmsg->header.last = 0x00;
22962306a36Sopenharmony_ci	wmsg->header.chan = 0x00;
23062306a36Sopenharmony_ci	wmsg->header.spi = 0x0000;
23162306a36Sopenharmony_ci	while (remain_len > 0) {
23262306a36Sopenharmony_ci		wmsg->header.addr = cpu_to_le16(start + 0x4000);
23362306a36Sopenharmony_ci		if (remain_len > 503) {
23462306a36Sopenharmony_ci			wmsg->header.len1 = 0xff;
23562306a36Sopenharmony_ci			wmsg->header.len2 = 0xf8;
23662306a36Sopenharmony_ci			remain_len -= 503;
23762306a36Sopenharmony_ci			bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr);
23862306a36Sopenharmony_ci			start += 503;
23962306a36Sopenharmony_ci		} else if (remain_len > 255) {
24062306a36Sopenharmony_ci			wmsg->header.len1 = 0xff;
24162306a36Sopenharmony_ci			wmsg->header.len2 = (remain_len - 255);
24262306a36Sopenharmony_ci			bytes_xfer = remain_len +
24362306a36Sopenharmony_ci				sizeof(struct vprbrd_i2c_write_hdr);
24462306a36Sopenharmony_ci			remain_len = 0;
24562306a36Sopenharmony_ci		} else {
24662306a36Sopenharmony_ci			wmsg->header.len1 = remain_len;
24762306a36Sopenharmony_ci			wmsg->header.len2 = 0x00;
24862306a36Sopenharmony_ci			bytes_xfer = remain_len +
24962306a36Sopenharmony_ci				sizeof(struct vprbrd_i2c_write_hdr);
25062306a36Sopenharmony_ci			remain_len = 0;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		memcpy(wmsg->data, msg->buf + start,
25362306a36Sopenharmony_ci			bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr));
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		ret = usb_bulk_msg(vb->usb_dev,
25662306a36Sopenharmony_ci			usb_sndbulkpipe(vb->usb_dev,
25762306a36Sopenharmony_ci			VPRBRD_EP_OUT), wmsg,
25862306a36Sopenharmony_ci			bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
25962306a36Sopenharmony_ci		if ((ret < 0) || (bytes_xfer != bytes_actual))
26062306a36Sopenharmony_ci			return -EREMOTEIO;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
26662306a36Sopenharmony_ci		int num)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct i2c_msg *pmsg;
26962306a36Sopenharmony_ci	int i, ret,
27062306a36Sopenharmony_ci		error = 0;
27162306a36Sopenharmony_ci	struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
27262306a36Sopenharmony_ci	struct vprbrd_i2c_addr_msg *amsg =
27362306a36Sopenharmony_ci		(struct vprbrd_i2c_addr_msg *)vb->buf;
27462306a36Sopenharmony_ci	struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	dev_dbg(&i2c->dev, "master xfer %d messages:\n", num);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	for (i = 0 ; i < num ; i++) {
27962306a36Sopenharmony_ci		pmsg = &msgs[i];
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		dev_dbg(&i2c->dev,
28262306a36Sopenharmony_ci			"  %d: %s (flags %d) %d bytes to 0x%02x\n",
28362306a36Sopenharmony_ci			i, pmsg->flags & I2C_M_RD ? "read" : "write",
28462306a36Sopenharmony_ci			pmsg->flags, pmsg->len, pmsg->addr);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		mutex_lock(&vb->lock);
28762306a36Sopenharmony_ci		/* directly send the message */
28862306a36Sopenharmony_ci		if (pmsg->flags & I2C_M_RD) {
28962306a36Sopenharmony_ci			/* read data */
29062306a36Sopenharmony_ci			amsg->cmd = VPRBRD_I2C_CMD_ADDR;
29162306a36Sopenharmony_ci			amsg->unknown2 = 0x00;
29262306a36Sopenharmony_ci			amsg->unknown3 = 0x00;
29362306a36Sopenharmony_ci			amsg->addr = pmsg->addr;
29462306a36Sopenharmony_ci			amsg->unknown1 = 0x01;
29562306a36Sopenharmony_ci			amsg->len = cpu_to_le16(pmsg->len);
29662306a36Sopenharmony_ci			/* send the addr and len, we're interested to board */
29762306a36Sopenharmony_ci			ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
29862306a36Sopenharmony_ci			if (ret < 0)
29962306a36Sopenharmony_ci				error = ret;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci			ret = vprbrd_i2c_read(vb, pmsg);
30262306a36Sopenharmony_ci			if (ret < 0)
30362306a36Sopenharmony_ci				error = ret;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci			ret = vprbrd_i2c_status(i2c, smsg, error);
30662306a36Sopenharmony_ci			if (ret < 0)
30762306a36Sopenharmony_ci				error = ret;
30862306a36Sopenharmony_ci			/* in case of protocol error, return the error */
30962306a36Sopenharmony_ci			if (error < 0)
31062306a36Sopenharmony_ci				goto error;
31162306a36Sopenharmony_ci		} else {
31262306a36Sopenharmony_ci			/* write data */
31362306a36Sopenharmony_ci			ret = vprbrd_i2c_write(vb, pmsg);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci			amsg->cmd = VPRBRD_I2C_CMD_ADDR;
31662306a36Sopenharmony_ci			amsg->unknown2 = 0x00;
31762306a36Sopenharmony_ci			amsg->unknown3 = 0x00;
31862306a36Sopenharmony_ci			amsg->addr = pmsg->addr;
31962306a36Sopenharmony_ci			amsg->unknown1 = 0x00;
32062306a36Sopenharmony_ci			amsg->len = cpu_to_le16(pmsg->len);
32162306a36Sopenharmony_ci			/* send the addr, the data goes to to board */
32262306a36Sopenharmony_ci			ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
32362306a36Sopenharmony_ci			if (ret < 0)
32462306a36Sopenharmony_ci				error = ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci			ret = vprbrd_i2c_status(i2c, smsg, error);
32762306a36Sopenharmony_ci			if (ret < 0)
32862306a36Sopenharmony_ci				error = ret;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci			if (error < 0)
33162306a36Sopenharmony_ci				goto error;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		mutex_unlock(&vb->lock);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	return num;
33662306a36Sopenharmony_cierror:
33762306a36Sopenharmony_ci	mutex_unlock(&vb->lock);
33862306a36Sopenharmony_ci	return error;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic u32 vprbrd_i2c_func(struct i2c_adapter *i2c)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/* This is the actual algorithm we define */
34762306a36Sopenharmony_cistatic const struct i2c_algorithm vprbrd_algorithm = {
34862306a36Sopenharmony_ci	.master_xfer	= vprbrd_i2c_xfer,
34962306a36Sopenharmony_ci	.functionality	= vprbrd_i2c_func,
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic const struct i2c_adapter_quirks vprbrd_quirks = {
35362306a36Sopenharmony_ci	.max_read_len = 2048,
35462306a36Sopenharmony_ci	.max_write_len = 2048,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int vprbrd_i2c_probe(struct platform_device *pdev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
36062306a36Sopenharmony_ci	struct vprbrd_i2c *vb_i2c;
36162306a36Sopenharmony_ci	int ret;
36262306a36Sopenharmony_ci	int pipe;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	vb_i2c = devm_kzalloc(&pdev->dev, sizeof(*vb_i2c), GFP_KERNEL);
36562306a36Sopenharmony_ci	if (vb_i2c == NULL)
36662306a36Sopenharmony_ci		return -ENOMEM;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* setup i2c adapter description */
36962306a36Sopenharmony_ci	vb_i2c->i2c.owner = THIS_MODULE;
37062306a36Sopenharmony_ci	vb_i2c->i2c.class = I2C_CLASS_HWMON;
37162306a36Sopenharmony_ci	vb_i2c->i2c.algo = &vprbrd_algorithm;
37262306a36Sopenharmony_ci	vb_i2c->i2c.quirks = &vprbrd_quirks;
37362306a36Sopenharmony_ci	vb_i2c->i2c.algo_data = vb;
37462306a36Sopenharmony_ci	/* save the param in usb capabable memory */
37562306a36Sopenharmony_ci	vb_i2c->bus_freq_param = i2c_bus_param;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name),
37862306a36Sopenharmony_ci		 "viperboard at bus %03d device %03d",
37962306a36Sopenharmony_ci		 vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* setting the bus frequency */
38262306a36Sopenharmony_ci	if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ)
38362306a36Sopenharmony_ci		&& (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) {
38462306a36Sopenharmony_ci		pipe = usb_sndctrlpipe(vb->usb_dev, 0);
38562306a36Sopenharmony_ci		ret = usb_control_msg(vb->usb_dev, pipe,
38662306a36Sopenharmony_ci			VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
38762306a36Sopenharmony_ci			0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
38862306a36Sopenharmony_ci			VPRBRD_USB_TIMEOUT_MS);
38962306a36Sopenharmony_ci		if (ret != 1) {
39062306a36Sopenharmony_ci			dev_err(&pdev->dev, "failure setting i2c_bus_freq to %d\n",
39162306a36Sopenharmony_ci				i2c_bus_freq);
39262306a36Sopenharmony_ci			return -EIO;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci	} else {
39562306a36Sopenharmony_ci		dev_err(&pdev->dev,
39662306a36Sopenharmony_ci			"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
39762306a36Sopenharmony_ci		return -EIO;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	vb_i2c->i2c.dev.parent = &pdev->dev;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* attach to i2c layer */
40362306a36Sopenharmony_ci	i2c_add_adapter(&vb_i2c->i2c);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	platform_set_drvdata(pdev, vb_i2c);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void vprbrd_i2c_remove(struct platform_device *pdev)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	i2c_del_adapter(&vb_i2c->i2c);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic struct platform_driver vprbrd_i2c_driver = {
41862306a36Sopenharmony_ci	.driver.name	= "viperboard-i2c",
41962306a36Sopenharmony_ci	.driver.owner	= THIS_MODULE,
42062306a36Sopenharmony_ci	.probe		= vprbrd_i2c_probe,
42162306a36Sopenharmony_ci	.remove_new	= vprbrd_i2c_remove,
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int __init vprbrd_i2c_init(void)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	switch (i2c_bus_freq) {
42762306a36Sopenharmony_ci	case 6000:
42862306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ;
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	case 3000:
43162306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ;
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	case 1000:
43462306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	case 400:
43762306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ;
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case 200:
44062306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ;
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	case 100:
44362306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci	case 10:
44662306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	default:
44962306a36Sopenharmony_ci		pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq);
45062306a36Sopenharmony_ci		i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return platform_driver_register(&vprbrd_i2c_driver);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_cisubsys_initcall(vprbrd_i2c_init);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void __exit vprbrd_i2c_exit(void)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	platform_driver_unregister(&vprbrd_i2c_driver);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_cimodule_exit(vprbrd_i2c_exit);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ciMODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
46462306a36Sopenharmony_ciMODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard");
46562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
46662306a36Sopenharmony_ciMODULE_ALIAS("platform:viperboard-i2c");
467