mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-17 22:05:08 +00:00
e70140ba0d
The continual trickle of small conversion patches is grating on me, and is really not helping. Just get rid of the 'remove_new' member function, which is just an alias for the plain 'remove', and had a comment to that effect: /* * .remove_new() is a relic from a prototype conversion of .remove(). * New drivers are supposed to implement .remove(). Once all drivers are * converted to not use .remove_new any more, it will be dropped. */ This was just a tree-wide 'sed' script that replaced '.remove_new' with '.remove', with some care taken to turn a subsequent tab into two tabs to make things line up. I did do some minimal manual whitespace adjustment for places that used spaces to line things up. Then I just removed the old (sic) .remove_new member function, and this is the end result. No more unnecessary conversion noise. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
407 lines
9.6 KiB
C
407 lines
9.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Congatec Board Controller I2C busses driver
|
|
*
|
|
* Copyright (C) 2024 Bootlin
|
|
* Author: Thomas Richard <thomas.richard@bootlin.com>
|
|
*/
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/mfd/cgbc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#define CGBC_I2C_PRIMARY_BUS_ID 0
|
|
#define CGBC_I2C_PM_BUS_ID 4
|
|
|
|
#define CGBC_I2C_CMD_START 0x40
|
|
#define CGBC_I2C_CMD_STAT 0x48
|
|
#define CGBC_I2C_CMD_DATA 0x50
|
|
#define CGBC_I2C_CMD_SPEED 0x58
|
|
|
|
#define CGBC_I2C_STAT_IDL 0x00
|
|
#define CGBC_I2C_STAT_DAT 0x01
|
|
#define CGBC_I2C_STAT_BUSY 0x02
|
|
|
|
#define CGBC_I2C_START 0x80
|
|
#define CGBC_I2C_STOP 0x40
|
|
|
|
#define CGBC_I2C_LAST_ACK 0x80 /* send ACK on last read byte */
|
|
|
|
/*
|
|
* Reference code defines 1kHz as min freq and 6.1MHz as max freq.
|
|
* But in practice, the board controller limits the frequency to 1MHz, and the
|
|
* 1kHz is not functional (minimal working freq is 50kHz).
|
|
* So use these values as limits.
|
|
*/
|
|
#define CGBC_I2C_FREQ_MIN_HZ 50000 /* 50 kHz */
|
|
#define CGBC_I2C_FREQ_MAX_HZ 1000000 /* 1 MHz */
|
|
|
|
#define CGBC_I2C_FREQ_UNIT_1KHZ 0x40
|
|
#define CGBC_I2C_FREQ_UNIT_10KHZ 0x80
|
|
#define CGBC_I2C_FREQ_UNIT_100KHZ 0xC0
|
|
|
|
#define CGBC_I2C_FREQ_UNIT_MASK 0xC0
|
|
#define CGBC_I2C_FREQ_VALUE_MASK 0x3F
|
|
|
|
#define CGBC_I2C_READ_MAX_LEN 31
|
|
#define CGBC_I2C_WRITE_MAX_LEN 32
|
|
|
|
#define CGBC_I2C_CMD_HEADER_SIZE 4
|
|
#define CGBC_I2C_CMD_SIZE (CGBC_I2C_CMD_HEADER_SIZE + CGBC_I2C_WRITE_MAX_LEN)
|
|
|
|
enum cgbc_i2c_state {
|
|
CGBC_I2C_STATE_DONE = 0,
|
|
CGBC_I2C_STATE_INIT,
|
|
CGBC_I2C_STATE_START,
|
|
CGBC_I2C_STATE_READ,
|
|
CGBC_I2C_STATE_WRITE,
|
|
CGBC_I2C_STATE_ERROR,
|
|
};
|
|
|
|
struct i2c_algo_cgbc_data {
|
|
u8 bus_id;
|
|
unsigned long read_maxtime_us;
|
|
};
|
|
|
|
struct cgbc_i2c_data {
|
|
struct device *dev;
|
|
struct cgbc_device_data *cgbc;
|
|
struct i2c_adapter adap;
|
|
struct i2c_msg *msg;
|
|
int nmsgs;
|
|
int pos;
|
|
enum cgbc_i2c_state state;
|
|
};
|
|
|
|
struct cgbc_i2c_transfer {
|
|
u8 bus_id;
|
|
bool start;
|
|
bool stop;
|
|
bool last_ack;
|
|
u8 read;
|
|
u8 write;
|
|
u8 addr;
|
|
u8 data[CGBC_I2C_WRITE_MAX_LEN];
|
|
};
|
|
|
|
static u8 cgbc_i2c_freq_to_reg(unsigned int bus_frequency)
|
|
{
|
|
u8 reg;
|
|
|
|
if (bus_frequency <= 10000)
|
|
reg = CGBC_I2C_FREQ_UNIT_1KHZ | (bus_frequency / 1000);
|
|
else if (bus_frequency <= 100000)
|
|
reg = CGBC_I2C_FREQ_UNIT_10KHZ | (bus_frequency / 10000);
|
|
else
|
|
reg = CGBC_I2C_FREQ_UNIT_100KHZ | (bus_frequency / 100000);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static unsigned int cgbc_i2c_reg_to_freq(u8 reg)
|
|
{
|
|
unsigned int freq = reg & CGBC_I2C_FREQ_VALUE_MASK;
|
|
u8 unit = reg & CGBC_I2C_FREQ_UNIT_MASK;
|
|
|
|
if (unit == CGBC_I2C_FREQ_UNIT_100KHZ)
|
|
return freq * 100000;
|
|
else if (unit == CGBC_I2C_FREQ_UNIT_10KHZ)
|
|
return freq * 10000;
|
|
else
|
|
return freq * 1000;
|
|
}
|
|
|
|
static int cgbc_i2c_get_status(struct i2c_adapter *adap)
|
|
{
|
|
struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
|
|
struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
|
|
struct cgbc_device_data *cgbc = i2c->cgbc;
|
|
u8 cmd = CGBC_I2C_CMD_STAT | algo_data->bus_id;
|
|
u8 status;
|
|
int ret;
|
|
|
|
ret = cgbc_command(cgbc, &cmd, sizeof(cmd), NULL, 0, &status);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return status;
|
|
}
|
|
|
|
static int cgbc_i2c_set_frequency(struct i2c_adapter *adap,
|
|
unsigned int bus_frequency)
|
|
{
|
|
struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
|
|
struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
|
|
struct cgbc_device_data *cgbc = i2c->cgbc;
|
|
u8 cmd[2], data;
|
|
int ret;
|
|
|
|
if (bus_frequency > CGBC_I2C_FREQ_MAX_HZ ||
|
|
bus_frequency < CGBC_I2C_FREQ_MIN_HZ) {
|
|
dev_info(i2c->dev, "invalid frequency %u, using default\n", bus_frequency);
|
|
bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;
|
|
}
|
|
|
|
cmd[0] = CGBC_I2C_CMD_SPEED | algo_data->bus_id;
|
|
cmd[1] = cgbc_i2c_freq_to_reg(bus_frequency);
|
|
|
|
ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);
|
|
if (ret)
|
|
return dev_err_probe(i2c->dev, ret,
|
|
"Failed to initialize I2C bus %s",
|
|
adap->name);
|
|
|
|
cmd[1] = 0x00;
|
|
|
|
ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);
|
|
if (ret)
|
|
return dev_err_probe(i2c->dev, ret,
|
|
"Failed to get I2C bus frequency");
|
|
|
|
bus_frequency = cgbc_i2c_reg_to_freq(data);
|
|
|
|
dev_dbg(i2c->dev, "%s is running at %d Hz\n", adap->name, bus_frequency);
|
|
|
|
/*
|
|
* The read_maxtime_us variable represents the maximum time to wait
|
|
* for data during a read operation. The maximum amount of data that
|
|
* can be read by a command is CGBC_I2C_READ_MAX_LEN.
|
|
* Therefore, calculate the max time to properly size the timeout.
|
|
*/
|
|
algo_data->read_maxtime_us = (BITS_PER_BYTE + 1) * CGBC_I2C_READ_MAX_LEN
|
|
* USEC_PER_SEC / bus_frequency;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int cgbc_i2c_xfer_to_cmd(struct cgbc_i2c_transfer xfer, u8 *cmd)
|
|
{
|
|
int i = 0;
|
|
|
|
cmd[i++] = CGBC_I2C_CMD_START | xfer.bus_id;
|
|
|
|
cmd[i] = (xfer.start) ? CGBC_I2C_START : 0x00;
|
|
if (xfer.stop)
|
|
cmd[i] |= CGBC_I2C_STOP;
|
|
cmd[i++] |= (xfer.start) ? xfer.write + 1 : xfer.write;
|
|
|
|
cmd[i++] = (xfer.last_ack) ? (xfer.read | CGBC_I2C_LAST_ACK) : xfer.read;
|
|
|
|
if (xfer.start)
|
|
cmd[i++] = xfer.addr;
|
|
|
|
if (xfer.write > 0)
|
|
memcpy(&cmd[i], &xfer.data, xfer.write);
|
|
|
|
return i + xfer.write;
|
|
}
|
|
|
|
static int cgbc_i2c_xfer_msg(struct i2c_adapter *adap)
|
|
{
|
|
struct i2c_algo_cgbc_data *algo_data = adap->algo_data;
|
|
struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
|
|
struct cgbc_device_data *cgbc = i2c->cgbc;
|
|
struct i2c_msg *msg = i2c->msg;
|
|
u8 cmd[CGBC_I2C_CMD_SIZE];
|
|
int ret, max_len, len, i;
|
|
unsigned int cmd_len;
|
|
u8 cmd_data;
|
|
|
|
struct cgbc_i2c_transfer xfer = {
|
|
.bus_id = algo_data->bus_id,
|
|
.addr = i2c_8bit_addr_from_msg(msg),
|
|
};
|
|
|
|
if (i2c->state == CGBC_I2C_STATE_DONE)
|
|
return 0;
|
|
|
|
ret = cgbc_i2c_get_status(adap);
|
|
|
|
if (ret == CGBC_I2C_STAT_BUSY)
|
|
return -EBUSY;
|
|
else if (ret < 0)
|
|
goto err;
|
|
|
|
if (i2c->state == CGBC_I2C_STATE_INIT ||
|
|
(i2c->state == CGBC_I2C_STATE_WRITE && msg->flags & I2C_M_RD))
|
|
xfer.start = true;
|
|
|
|
i2c->state = (msg->flags & I2C_M_RD) ? CGBC_I2C_STATE_READ : CGBC_I2C_STATE_WRITE;
|
|
|
|
max_len = (i2c->state == CGBC_I2C_STATE_READ) ?
|
|
CGBC_I2C_READ_MAX_LEN : CGBC_I2C_WRITE_MAX_LEN;
|
|
|
|
if (msg->len - i2c->pos > max_len) {
|
|
len = max_len;
|
|
} else {
|
|
len = msg->len - i2c->pos;
|
|
|
|
if (i2c->nmsgs == 1)
|
|
xfer.stop = true;
|
|
}
|
|
|
|
if (i2c->state == CGBC_I2C_STATE_WRITE) {
|
|
xfer.write = len;
|
|
xfer.read = 0;
|
|
|
|
for (i = 0; i < len; i++)
|
|
xfer.data[i] = msg->buf[i2c->pos + i];
|
|
|
|
cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);
|
|
|
|
ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);
|
|
if (ret)
|
|
goto err;
|
|
} else if (i2c->state == CGBC_I2C_STATE_READ) {
|
|
xfer.write = 0;
|
|
xfer.read = len;
|
|
|
|
if (i2c->nmsgs > 1 || msg->len - i2c->pos > max_len)
|
|
xfer.read |= CGBC_I2C_LAST_ACK;
|
|
|
|
cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);
|
|
ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = read_poll_timeout(cgbc_i2c_get_status, ret,
|
|
ret != CGBC_I2C_STAT_BUSY, 0,
|
|
2 * algo_data->read_maxtime_us, false, adap);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
cmd_data = CGBC_I2C_CMD_DATA | algo_data->bus_id;
|
|
ret = cgbc_command(cgbc, &cmd_data, sizeof(cmd_data),
|
|
msg->buf + i2c->pos, len, NULL);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
|
|
if (len == (msg->len - i2c->pos)) {
|
|
i2c->msg++;
|
|
i2c->nmsgs--;
|
|
i2c->pos = 0;
|
|
} else {
|
|
i2c->pos += len;
|
|
}
|
|
|
|
if (i2c->nmsgs == 0)
|
|
i2c->state = CGBC_I2C_STATE_DONE;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
i2c->state = CGBC_I2C_STATE_ERROR;
|
|
return ret;
|
|
}
|
|
|
|
static int cgbc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|
int num)
|
|
{
|
|
struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);
|
|
unsigned long timeout = jiffies + HZ;
|
|
int ret;
|
|
|
|
i2c->state = CGBC_I2C_STATE_INIT;
|
|
i2c->msg = msgs;
|
|
i2c->nmsgs = num;
|
|
i2c->pos = 0;
|
|
|
|
while (time_before(jiffies, timeout)) {
|
|
ret = cgbc_i2c_xfer_msg(adap);
|
|
if (i2c->state == CGBC_I2C_STATE_DONE)
|
|
return num;
|
|
|
|
if (i2c->state == CGBC_I2C_STATE_ERROR)
|
|
return ret;
|
|
|
|
if (ret == 0)
|
|
timeout = jiffies + HZ;
|
|
}
|
|
|
|
i2c->state = CGBC_I2C_STATE_ERROR;
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
static u32 cgbc_i2c_func(struct i2c_adapter *adap)
|
|
{
|
|
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~(I2C_FUNC_SMBUS_QUICK));
|
|
}
|
|
|
|
static const struct i2c_algorithm cgbc_i2c_algorithm = {
|
|
.master_xfer = cgbc_i2c_xfer,
|
|
.functionality = cgbc_i2c_func,
|
|
};
|
|
|
|
static struct i2c_algo_cgbc_data cgbc_i2c_algo_data[] = {
|
|
{ .bus_id = CGBC_I2C_PRIMARY_BUS_ID },
|
|
{ .bus_id = CGBC_I2C_PM_BUS_ID },
|
|
};
|
|
|
|
static const struct i2c_adapter cgbc_i2c_adapter[] = {
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.name = "Congatec General Purpose I2C adapter",
|
|
.class = I2C_CLASS_DEPRECATED,
|
|
.algo = &cgbc_i2c_algorithm,
|
|
.algo_data = &cgbc_i2c_algo_data[0],
|
|
.nr = -1,
|
|
},
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.name = "Congatec Power Management I2C adapter",
|
|
.class = I2C_CLASS_DEPRECATED,
|
|
.algo = &cgbc_i2c_algorithm,
|
|
.algo_data = &cgbc_i2c_algo_data[1],
|
|
.nr = -1,
|
|
},
|
|
};
|
|
|
|
static int cgbc_i2c_probe(struct platform_device *pdev)
|
|
{
|
|
struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
|
|
struct cgbc_i2c_data *i2c;
|
|
int ret;
|
|
|
|
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
|
if (!i2c)
|
|
return -ENOMEM;
|
|
|
|
i2c->cgbc = cgbc;
|
|
i2c->dev = &pdev->dev;
|
|
i2c->adap = cgbc_i2c_adapter[pdev->id];
|
|
i2c->adap.dev.parent = i2c->dev;
|
|
i2c_set_adapdata(&i2c->adap, i2c);
|
|
platform_set_drvdata(pdev, i2c);
|
|
|
|
ret = cgbc_i2c_set_frequency(&i2c->adap, I2C_MAX_STANDARD_MODE_FREQ);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return i2c_add_numbered_adapter(&i2c->adap);
|
|
}
|
|
|
|
static void cgbc_i2c_remove(struct platform_device *pdev)
|
|
{
|
|
struct cgbc_i2c_data *i2c = platform_get_drvdata(pdev);
|
|
|
|
i2c_del_adapter(&i2c->adap);
|
|
}
|
|
|
|
static struct platform_driver cgbc_i2c_driver = {
|
|
.driver = {
|
|
.name = "cgbc-i2c",
|
|
},
|
|
.probe = cgbc_i2c_probe,
|
|
.remove = cgbc_i2c_remove,
|
|
};
|
|
|
|
module_platform_driver(cgbc_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("Congatec Board Controller I2C Driver");
|
|
MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:cgbc_i2c");
|