Commit b61d1575 authored by Andrew Lunn's avatar Andrew Lunn Committed by Wolfram Sang

I2C: MV64XYZ: Add Device Tree support

Extends the driver to get properties from device tree. Rather than
pass the N & M factors in DT, use the more standard clock-frequency
property. Calculate N & M at run time. In order to do this, we need to
know tclk. So the driver uses clk_get() etc in order to get the clock
and clk_get_rate() to determine the tclk rate. Not all platforms
however have CLK, so some #ifdefery is needed to ensure the driver
still compiles when CLK is not available.
Signed-off-by: default avatarAndrew Lunn <andrew@lunn.ch>

[wsa: converted some ints to u32 to match signedness]
Signed-off-by: default avatarWolfram Sang <w.sang@pengutronix.de>
parent 6f535b94
* I2C * Marvell MMP I2C controller
Required properties : Required properties :
...@@ -32,3 +32,20 @@ Examples: ...@@ -32,3 +32,20 @@ Examples:
interrupts = <58>; interrupts = <58>;
}; };
* Marvell MV64XXX I2C controller
Required properties :
- reg : Offset and length of the register set for the device
- compatible : Should be "marvell,mv64xxx-i2c"
- interrupts : The interrupt number
- clock-frequency : Desired I2C bus clock frequency in Hz.
Examples:
i2c@11000 {
compatible = "marvell,mv64xxx-i2c";
reg = <0x11000 0x20>;
interrupts = <29>;
clock-frequency = <100000>;
};
...@@ -18,6 +18,11 @@ ...@@ -18,6 +18,11 @@
#include <linux/mv643xx_i2c.h> #include <linux/mv643xx_i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_i2c.h>
#include <linux/clk.h>
#include <linux/err.h>
/* Register defines */ /* Register defines */
#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00 #define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
...@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data { ...@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
int rc; int rc;
u32 freq_m; u32 freq_m;
u32 freq_n; u32 freq_n;
#if defined(CONFIG_HAVE_CLK)
struct clk *clk;
#endif
wait_queue_head_t waitq; wait_queue_head_t waitq;
spinlock_t lock; spinlock_t lock;
struct i2c_msg *msg; struct i2c_msg *msg;
...@@ -521,6 +529,82 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data) ...@@ -521,6 +529,82 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
drv_data->reg_base_p = 0; drv_data->reg_base_p = 0;
} }
#ifdef CONFIG_OF
static int __devinit
mv64xxx_calc_freq(const int tclk, const int n, const int m)
{
return tclk / (10 * (m + 1) * (2 << n));
}
static bool __devinit
mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
u32 *best_m)
{
int freq, delta, best_delta = INT_MAX;
int m, n;
for (n = 0; n <= 7; n++)
for (m = 0; m <= 15; m++) {
freq = mv64xxx_calc_freq(tclk, n, m);
delta = req_freq - freq;
if (delta >= 0 && delta < best_delta) {
*best_m = m;
*best_n = n;
best_delta = delta;
}
if (best_delta == 0)
return true;
}
if (best_delta == INT_MAX)
return false;
return true;
}
static int __devinit
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
struct device_node *np)
{
u32 bus_freq, tclk;
int rc = 0;
/* CLK is mandatory when using DT to describe the i2c bus. We
* need to know tclk in order to calculate bus clock
* factors.
*/
#if !defined(CONFIG_HAVE_CLK)
/* Have OF but no CLK */
return -ENODEV;
#else
if (IS_ERR(drv_data->clk)) {
rc = -ENODEV;
goto out;
}
tclk = clk_get_rate(drv_data->clk);
of_property_read_u32(np, "clock-frequency", &bus_freq);
if (!mv64xxx_find_baud_factors(bus_freq, tclk,
&drv_data->freq_n, &drv_data->freq_m)) {
rc = -EINVAL;
goto out;
}
drv_data->irq = irq_of_parse_and_map(np, 0);
/* Its not yet defined how timeouts will be specified in device tree.
* So hard code the value to 1 second.
*/
drv_data->adapter.timeout = HZ;
out:
return rc;
#endif
}
#else /* CONFIG_OF */
static int __devinit
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
struct device_node *np)
{
return -ENODEV;
}
#endif /* CONFIG_OF */
static int __devinit static int __devinit
mv64xxx_i2c_probe(struct platform_device *pd) mv64xxx_i2c_probe(struct platform_device *pd)
{ {
...@@ -528,7 +612,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) ...@@ -528,7 +612,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data; struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data;
int rc; int rc;
if (!pdata) if ((!pdata && !pd->dev.of_node))
return -ENODEV; return -ENODEV;
drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL); drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
...@@ -546,19 +630,35 @@ mv64xxx_i2c_probe(struct platform_device *pd) ...@@ -546,19 +630,35 @@ mv64xxx_i2c_probe(struct platform_device *pd)
init_waitqueue_head(&drv_data->waitq); init_waitqueue_head(&drv_data->waitq);
spin_lock_init(&drv_data->lock); spin_lock_init(&drv_data->lock);
drv_data->freq_m = pdata->freq_m; #if defined(CONFIG_HAVE_CLK)
drv_data->freq_n = pdata->freq_n; /* Not all platforms have a clk */
drv_data->irq = platform_get_irq(pd, 0); drv_data->clk = clk_get(&pd->dev, NULL);
if (!IS_ERR(drv_data->clk)) {
clk_prepare(drv_data->clk);
clk_enable(drv_data->clk);
}
#endif
if (pdata) {
drv_data->freq_m = pdata->freq_m;
drv_data->freq_n = pdata->freq_n;
drv_data->irq = platform_get_irq(pd, 0);
drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
} else if (pd->dev.of_node) {
rc = mv64xxx_of_config(drv_data, pd->dev.of_node);
if (rc)
goto exit_unmap_regs;
}
if (drv_data->irq < 0) { if (drv_data->irq < 0) {
rc = -ENXIO; rc = -ENXIO;
goto exit_unmap_regs; goto exit_unmap_regs;
} }
drv_data->adapter.dev.parent = &pd->dev; drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo; drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE; drv_data->adapter.owner = THIS_MODULE;
drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
drv_data->adapter.nr = pd->id; drv_data->adapter.nr = pd->id;
drv_data->adapter.dev.of_node = pd->dev.of_node;
platform_set_drvdata(pd, drv_data); platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data); i2c_set_adapdata(&drv_data->adapter, drv_data);
...@@ -577,11 +677,20 @@ mv64xxx_i2c_probe(struct platform_device *pd) ...@@ -577,11 +677,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_free_irq; goto exit_free_irq;
} }
of_i2c_register_devices(&drv_data->adapter);
return 0; return 0;
exit_free_irq: exit_free_irq:
free_irq(drv_data->irq, drv_data); free_irq(drv_data->irq, drv_data);
exit_unmap_regs: exit_unmap_regs:
#if defined(CONFIG_HAVE_CLK)
/* Not all platforms have a clk */
if (!IS_ERR(drv_data->clk)) {
clk_disable(drv_data->clk);
clk_unprepare(drv_data->clk);
}
#endif
mv64xxx_i2c_unmap_regs(drv_data); mv64xxx_i2c_unmap_regs(drv_data);
exit_kfree: exit_kfree:
kfree(drv_data); kfree(drv_data);
...@@ -597,17 +706,31 @@ mv64xxx_i2c_remove(struct platform_device *dev) ...@@ -597,17 +706,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
rc = i2c_del_adapter(&drv_data->adapter); rc = i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data); free_irq(drv_data->irq, drv_data);
mv64xxx_i2c_unmap_regs(drv_data); mv64xxx_i2c_unmap_regs(drv_data);
#if defined(CONFIG_HAVE_CLK)
/* Not all platforms have a clk */
if (!IS_ERR(drv_data->clk)) {
clk_disable(drv_data->clk);
clk_unprepare(drv_data->clk);
}
#endif
kfree(drv_data); kfree(drv_data);
return rc; return rc;
} }
static const struct of_device_id mv64xxx_i2c_of_match_table[] __devinitdata = {
{ .compatible = "marvell,mv64xxx-i2c", },
{}
};
MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
static struct platform_driver mv64xxx_i2c_driver = { static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe, .probe = mv64xxx_i2c_probe,
.remove = __devexit_p(mv64xxx_i2c_remove), .remove = __devexit_p(mv64xxx_i2c_remove),
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = MV64XXX_I2C_CTLR_NAME, .name = MV64XXX_I2C_CTLR_NAME,
.of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment