Skip to content

Commit

Permalink
serial: max310x: implement I2C support
Browse files Browse the repository at this point in the history
I2C implementation on this chip has a few key differences
compared to SPI, as described in previous patches.
 * extended register space access needs no extra logic
 * slave address is used to select which UART to communicate
   with

To accommodate these differences, add an I2C interface config,
set the RevID register address and implement an empty method
for setting the GlobalCommand register, since no special handling
is needed for the extended register space.

To handle the port-specific slave address, create an I2C dummy
device for each port, except the base one (UART0), which is
expected to be the one specified in firmware, and create a
regmap for each I2C device.
Add minimum and maximum slave addresses to each devtype for
sanity checking.

Also, use a separate regmap config with no write_flag_mask,
since I2C has a R/W bit in its slave address, and set the
max register to the address of the RevID register, since the
extended register space needs no extra logic.

Finally, add the I2C driver.

Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Cosmin Tanislav <cosmin.tanislav@analog.com>
Link: https://lore.kernel.org/r/20220605144659.4169853-5-demonsingur@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Demon000 authored and gregkh committed Jun 10, 2022
1 parent b3883ab commit 2e1f2d9
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/tty/serial/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ config SERIAL_MAX310X
depends on SPI_MASTER
select SERIAL_CORE
select REGMAP_SPI if SPI_MASTER
select REGMAP_I2C if I2C
help
This selects support for an advanced UART from Maxim (Dallas).
Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
Expand Down
135 changes: 134 additions & 1 deletion drivers/tty/serial/max310x.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
Expand Down Expand Up @@ -73,6 +74,7 @@

/* Extended registers */
#define MAX310X_SPI_REVID_EXTREG MAX310X_REG_05 /* Revision ID */
#define MAX310X_I2C_REVID_EXTREG (0x25) /* Revision ID */

/* IRQ register bits */
#define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
Expand Down Expand Up @@ -252,6 +254,10 @@ struct max310x_if_cfg {
};

struct max310x_devtype {
struct {
unsigned short min;
unsigned short max;
} slave_addr;
char name[9];
int nr;
u8 mode1;
Expand Down Expand Up @@ -423,6 +429,10 @@ static const struct max310x_devtype max3107_devtype = {
.mode1 = MAX310X_MODE1_AUTOSLEEP_BIT | MAX310X_MODE1_IRQSEL_BIT,
.detect = max3107_detect,
.power = max310x_power,
.slave_addr = {
.min = 0x2c,
.max = 0x2f,
},
};

static const struct max310x_devtype max3108_devtype = {
Expand All @@ -431,6 +441,10 @@ static const struct max310x_devtype max3108_devtype = {
.mode1 = MAX310X_MODE1_AUTOSLEEP_BIT,
.detect = max3108_detect,
.power = max310x_power,
.slave_addr = {
.min = 0x60,
.max = 0x6f,
},
};

static const struct max310x_devtype max3109_devtype = {
Expand All @@ -439,6 +453,10 @@ static const struct max310x_devtype max3109_devtype = {
.mode1 = MAX310X_MODE1_AUTOSLEEP_BIT,
.detect = max3109_detect,
.power = max310x_power,
.slave_addr = {
.min = 0x60,
.max = 0x6f,
},
};

static const struct max310x_devtype max14830_devtype = {
Expand All @@ -447,6 +465,10 @@ static const struct max310x_devtype max14830_devtype = {
.mode1 = MAX310X_MODE1_IRQSEL_BIT,
.detect = max14830_detect,
.power = max14830_power,
.slave_addr = {
.min = 0x60,
.max = 0x6f,
},
};

static bool max310x_reg_writeable(struct device *dev, unsigned int reg)
Expand Down Expand Up @@ -1516,6 +1538,97 @@ static struct spi_driver max310x_spi_driver = {
};
#endif

#ifdef CONFIG_I2C
static int max310x_i2c_extended_reg_enable(struct device *dev, bool enable)
{
return 0;
}

static struct regmap_config regcfg_i2c = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.writeable_reg = max310x_reg_writeable,
.volatile_reg = max310x_reg_volatile,
.precious_reg = max310x_reg_precious,
.max_register = MAX310X_I2C_REVID_EXTREG,
};

static const struct max310x_if_cfg max310x_i2c_if_cfg = {
.extended_reg_enable = max310x_i2c_extended_reg_enable,
.rev_id_reg = MAX310X_I2C_REVID_EXTREG,
};

static unsigned short max310x_i2c_slave_addr(unsigned short addr,
unsigned int nr)
{
/*
* For MAX14830 and MAX3109, the slave address depends on what the
* A0 and A1 pins are tied to.
* See Table I2C Address Map of the datasheet.
* Based on that table, the following formulas were determined.
* UART1 - UART0 = 0x10
* UART2 - UART1 = 0x20 + 0x10
* UART3 - UART2 = 0x10
*/

addr -= nr * 0x10;

if (nr >= 2)
addr -= 0x20;

return addr;
}

static int max310x_i2c_probe(struct i2c_client *client)
{
const struct max310x_devtype *devtype =
device_get_match_data(&client->dev);
struct i2c_client *port_client;
struct regmap *regmaps[4];
unsigned int i;
u8 port_addr;

if (client->addr < devtype->slave_addr.min ||
client->addr > devtype->slave_addr.max)
return dev_err_probe(&client->dev, -EINVAL,
"Slave addr 0x%x outside of range [0x%x, 0x%x]\n",
client->addr, devtype->slave_addr.min,
devtype->slave_addr.max);

regmaps[0] = devm_regmap_init_i2c(client, &regcfg_i2c);

for (i = 1; i < devtype->nr; i++) {
port_addr = max310x_i2c_slave_addr(client->addr, i);
port_client = devm_i2c_new_dummy_device(&client->dev,
client->adapter,
port_addr);

regmaps[i] = devm_regmap_init_i2c(port_client, &regcfg_i2c);
}

return max310x_probe(&client->dev, devtype, &max310x_i2c_if_cfg,
regmaps, client->irq);
}

static int max310x_i2c_remove(struct i2c_client *client)
{
max310x_remove(&client->dev);

return 0;
}

static struct i2c_driver max310x_i2c_driver = {
.driver = {
.name = MAX310X_NAME,
.of_match_table = max310x_dt_ids,
.pm = &max310x_pm_ops,
},
.probe_new = max310x_i2c_probe,
.remove = max310x_i2c_remove,
};
#endif

static int __init max310x_uart_init(void)
{
int ret;
Expand All @@ -1529,15 +1642,35 @@ static int __init max310x_uart_init(void)
#ifdef CONFIG_SPI_MASTER
ret = spi_register_driver(&max310x_spi_driver);
if (ret)
uart_unregister_driver(&max310x_uart);
goto err_spi_register;
#endif

#ifdef CONFIG_I2C
ret = i2c_add_driver(&max310x_i2c_driver);
if (ret)
goto err_i2c_register;
#endif

return 0;

#ifdef CONFIG_I2C
err_i2c_register:
spi_unregister_driver(&max310x_spi_driver);
#endif

err_spi_register:
uart_unregister_driver(&max310x_uart);

return ret;
}
module_init(max310x_uart_init);

static void __exit max310x_uart_exit(void)
{
#ifdef CONFIG_I2C
i2c_del_driver(&max310x_i2c_driver);
#endif

#ifdef CONFIG_SPI_MASTER
spi_unregister_driver(&max310x_spi_driver);
#endif
Expand Down

0 comments on commit 2e1f2d9

Please sign in to comment.