@@ -12,6 +12,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/iopoll.h>
@@ -63,11 +64,19 @@
/* The default bus frequency, which is an empirical value */
#define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ)
+/* The div of i2c reference clock on LS2K0500/2K1000/2K2000 */
+#define LS2X_I2C_2K_CLOCK_DIV 40
+
+/* The div of i2c reference clock on LS7A1000/7A2000 */
+#define LS2X_I2C_7A_CLOCK_DIV 50
+
struct ls2x_i2c_priv {
struct i2c_adapter adapter;
void __iomem *base;
struct i2c_timings i2c_t;
struct completion cmd_complete;
+ unsigned int div;
+ unsigned int pclk;
};
/*
@@ -107,12 +116,14 @@ static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
else
t->bus_freq_hz = LS2X_I2C_FREQ_STD;
+ val = div64_u64((u64)priv->pclk * 10,
+ (u64)priv->div * t->bus_freq_hz) - 1;
+
/*
* 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;
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);
}
@@ -287,6 +298,7 @@ static const struct i2c_algorithm ls2x_i2c_algo = {
static int ls2x_i2c_probe(struct platform_device *pdev)
{
int ret, irq;
+ struct clk *clk;
struct i2c_adapter *adap;
struct ls2x_i2c_priv *priv;
struct device *dev = &pdev->dev;
@@ -304,6 +316,32 @@ static int ls2x_i2c_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ if (dev_of_node(dev)) {
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ if (clk) {
+ priv->pclk = clk_get_rate(clk);
+ if (!priv->pclk)
+ priv->pclk = LS2X_I2C_PCLK_FREQ;
+ } else {
+ priv->pclk = LS2X_I2C_PCLK_FREQ;
+ }
+
+ priv->div = (unsigned long)device_get_match_data(dev);
+ if (!priv->div)
+ priv->div = LS2X_I2C_2K_CLOCK_DIV;
+ } else {
+ /* clocks and clock-div properties are used only in ACPI path. */
+ ret = device_property_read_u32(dev, "clocks", &priv->pclk);
+ if (ret || !priv->pclk)
+ priv->pclk = LS2X_I2C_PCLK_FREQ;
+
+ ret = device_property_read_u32(dev, "clock-div", &priv->div);
+ if (ret || !priv->div)
+ priv->div = LS2X_I2C_7A_CLOCK_DIV;
+ }
+
/* Add the i2c adapter */
adap = &priv->adapter;
adap->retries = 5;
@@ -349,8 +387,8 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
static const struct of_device_id ls2x_i2c_id_table[] = {
- { .compatible = "loongson,ls2k-i2c" },
- { .compatible = "loongson,ls7a-i2c" },
+ { .compatible = "loongson,ls2k-i2c", .data = (void *)LS2X_I2C_2K_CLOCK_DIV, },
+ { .compatible = "loongson,ls7a-i2c", .data = (void *)LS2X_I2C_7A_CLOCK_DIV, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);