mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-26 04:57:12 +08:00
loongson: i2c: support clock and bus freq from dts or acpi
Changelog: 龙芯i2c驱动支持输入时钟和总线时钟从dts或acpi获取 Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2C_LS2X_PRER_LO 0x0 /* Freq Division Low Byte Register */
|
||||
@@ -68,6 +69,8 @@ struct ls2x_i2c_priv {
|
||||
void __iomem *base;
|
||||
struct i2c_timings i2c_t;
|
||||
struct completion cmd_complete;
|
||||
u32 bus_frequency;
|
||||
u64 clock_frequency;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -98,21 +101,16 @@ static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
|
||||
u16 val;
|
||||
struct i2c_timings *t = &priv->i2c_t;
|
||||
struct device *dev = priv->adapter.dev.parent;
|
||||
u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
|
||||
|
||||
i2c_parse_fw_timings(dev, t, false);
|
||||
|
||||
if (acpi_speed || t->bus_freq_hz)
|
||||
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
|
||||
else
|
||||
t->bus_freq_hz = LS2X_I2C_FREQ_STD;
|
||||
|
||||
t->bus_freq_hz = priv->bus_frequency;
|
||||
/*
|
||||
* According to the chip manual, we can only access the registers as bytes,
|
||||
* otherwise the high bits will be truncated.
|
||||
* So set the I2C frequency with a sequential writeb() instead of writew().
|
||||
*/
|
||||
val = LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1;
|
||||
val = priv->clock_frequency / (5 * t->bus_freq_hz) - 1;
|
||||
writeb(FIELD_GET(GENMASK(7, 0), val), priv->base + I2C_LS2X_PRER_LO);
|
||||
writeb(FIELD_GET(GENMASK(15, 8), val), priv->base + I2C_LS2X_PRER_HI);
|
||||
}
|
||||
@@ -284,6 +282,55 @@ static const struct i2c_algorithm ls2x_i2c_algo = {
|
||||
.functionality = ls2x_i2c_func,
|
||||
};
|
||||
|
||||
static void clock_frequency_get(struct platform_device *pdev, struct device *dev, struct ls2x_i2c_priv *priv)
|
||||
{
|
||||
int err;
|
||||
u32 clk;
|
||||
struct clk *clk_unit;
|
||||
|
||||
if (!(device_property_read_u32(dev, "clock-frequency", &clk)))
|
||||
priv->clock_frequency = clk;
|
||||
else {
|
||||
clk_unit = devm_clk_get(&pdev->dev, "i2c-clk");
|
||||
|
||||
if(IS_ERR(clk_unit))
|
||||
goto err_def;
|
||||
|
||||
err = clk_prepare_enable(clk_unit);
|
||||
if (err)
|
||||
goto err_def;
|
||||
|
||||
clk = clk_get_rate(clk_unit);
|
||||
if (!clk) {
|
||||
clk_disable_unprepare(clk_unit);
|
||||
goto err_def;
|
||||
}
|
||||
|
||||
priv->clock_frequency = clk;
|
||||
clk_unit = NULL;
|
||||
return;
|
||||
err_def:
|
||||
pr_info("i2c-ls2x %s warning: cant get i2c input clock, default value 125000000", pdev->name);
|
||||
priv->clock_frequency = 125000000;
|
||||
clk_unit = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void bus_frequency_get(struct platform_device *pdev, struct device *dev, struct ls2x_i2c_priv *priv)
|
||||
{
|
||||
u32 bus_freq;
|
||||
|
||||
priv->bus_frequency = 0;
|
||||
|
||||
if (!(device_property_read_u32(dev, "bus_freq", &bus_freq)))
|
||||
priv->bus_frequency = bus_freq;
|
||||
else
|
||||
priv->bus_frequency = i2c_acpi_find_bus_speed(dev);
|
||||
|
||||
if (!(priv->bus_frequency))
|
||||
priv->bus_frequency = 100000; // default 100KHz
|
||||
}
|
||||
|
||||
static int ls2x_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, irq;
|
||||
@@ -304,6 +351,11 @@ static int ls2x_i2c_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
clock_frequency_get(pdev, dev, priv);
|
||||
dev_info(dev, "i2c dev input clock is %lld Hz\n", priv->clock_frequency);
|
||||
bus_frequency_get(pdev, dev, priv);
|
||||
dev_info(dev, "i2c dev bus clock is %d Hz\n", priv->bus_frequency);
|
||||
|
||||
/* Add the i2c adapter */
|
||||
adap = &priv->adapter;
|
||||
adap->retries = 5;
|
||||
|
||||
Reference in New Issue
Block a user