* [PATCH] I2C: MV64XYZ: Add Device Tree support
@ 2012-07-20 17:00 Andrew Lunn
2012-07-21 12:55 ` Wolfram Sang
0 siblings, 1 reply; 5+ messages in thread
From: Andrew Lunn @ 2012-07-20 17:00 UTC (permalink / raw)
To: linux-arm-kernel
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: Andrew Lunn <andrew@lunn.ch>
---
- Replaced the timeout property in DT, with a hard coded 1 second.
- Put all the OF code together.
- Split the ARM parts from the driver itself.
- Dropped Sebastian Hesselbarth Acked-by, since the changes are not trivial.
- Change MV64XXX to MV64XYZ in the Subject to try to get it past the list
spam filter.
This patch and the ARM counter part patch, plus all the DT changes to
make us of it can be found in:
git://github.com/lunn/linux.git v3.5-rc7-for-next-v5
This patch is not yet rebased on i2c-embedded/for-next. I will do this
once there is a general acceptance of this patch.
Documentation/devicetree/bindings/i2c/mrvl-i2c.txt | 19 ++-
drivers/i2c/busses/i2c-mv64xxx.c | 135 +++++++++++++++++++-
2 files changed, 148 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
index b891ee2..0f79450 100644
--- a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
+++ b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
@@ -1,4 +1,4 @@
-* I2C
+* Marvell MMP I2C controller
Required properties :
@@ -32,3 +32,20 @@ Examples:
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 at 11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ interrupts = <29>;
+ clock-frequency = <100000>;
+ };
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 4f44a33..e5449be 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -18,6 +18,11 @@
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.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 */
#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
int rc;
u32 freq_m;
u32 freq_n;
+#if defined(CONFIG_HAVE_CLK)
+ struct clk *clk;
+#endif
wait_queue_head_t waitq;
spinlock_t lock;
struct i2c_msg *msg;
@@ -521,6 +529,83 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
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 int req_freq, const int tclk, int *best_n,
+ int *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)
+{
+ int bus_freq;
+ int 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 */
+ rc = -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
mv64xxx_i2c_probe(struct platform_device *pd)
{
@@ -528,7 +613,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data;
int rc;
- if ((pd->id != 0) || !pdata)
+ if ((!pdata && !pd->dev.of_node) || (pdata && (pd->id != 0)))
return -ENODEV;
drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
@@ -546,19 +631,36 @@ mv64xxx_i2c_probe(struct platform_device *pd)
init_waitqueue_head(&drv_data->waitq);
spin_lock_init(&drv_data->lock);
- drv_data->freq_m = pdata->freq_m;
- drv_data->freq_n = pdata->freq_n;
- drv_data->irq = platform_get_irq(pd, 0);
+#if defined(CONFIG_HAVE_CLK)
+ /* Not all platforms have a clk */
+ 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);
+ }
+ 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) {
rc = -ENXIO;
goto exit_unmap_regs;
}
+
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
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.dev.of_node = pd->dev.of_node;
platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
@@ -577,11 +679,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_free_irq;
}
+ of_i2c_register_devices(&drv_data->adapter);
+
return 0;
exit_free_irq:
free_irq(drv_data->irq, drv_data);
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);
exit_kfree:
kfree(drv_data);
@@ -597,17 +708,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
rc = i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, 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);
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 = {
.probe = mv64xxx_i2c_probe,
.remove = __devexit_p(mv64xxx_i2c_remove),
.driver = {
.owner = THIS_MODULE,
.name = MV64XXX_I2C_CTLR_NAME,
+ .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
},
};
--
1.7.10
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH] I2C: MV64XYZ: Add Device Tree support
2012-07-20 17:00 Andrew Lunn
@ 2012-07-21 12:55 ` Wolfram Sang
0 siblings, 0 replies; 5+ messages in thread
From: Wolfram Sang @ 2012-07-21 12:55 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Fri, Jul 20, 2012 at 07:00:57PM +0200, Andrew Lunn wrote:
> 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: Andrew Lunn <andrew@lunn.ch>
> ---
>
> - Replaced the timeout property in DT, with a hard coded 1 second.
> - Put all the OF code together.
> - Split the ARM parts from the driver itself.
> - Dropped Sebastian Hesselbarth Acked-by, since the changes are not trivial.
> - Change MV64XXX to MV64XYZ in the Subject to try to get it past the list
> spam filter.
:)
>
> This patch and the ARM counter part patch, plus all the DT changes to
> make us of it can be found in:
>
> git://github.com/lunn/linux.git v3.5-rc7-for-next-v5
>
> This patch is not yet rebased on i2c-embedded/for-next. I will do this
> once there is a general acceptance of this patch.
>
> Documentation/devicetree/bindings/i2c/mrvl-i2c.txt | 19 ++-
> drivers/i2c/busses/i2c-mv64xxx.c | 135 +++++++++++++++++++-
> 2 files changed, 148 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
> index b891ee2..0f79450 100644
> --- a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
> +++ b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
> @@ -1,4 +1,4 @@
> -* I2C
> +* Marvell MMP I2C controller
>
> Required properties :
>
> @@ -32,3 +32,20 @@ Examples:
> 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 at 11000 {
> + compatible = "marvell,mv64xxx-i2c";
> + reg = <0x11000 0x20>;
> + interrupts = <29>;
> + clock-frequency = <100000>;
> + };
> diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
> index 4f44a33..e5449be 100644
> --- a/drivers/i2c/busses/i2c-mv64xxx.c
> +++ b/drivers/i2c/busses/i2c-mv64xxx.c
> @@ -18,6 +18,11 @@
> #include <linux/mv643xx_i2c.h>
> #include <linux/platform_device.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 */
> #define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
> @@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
> int rc;
> u32 freq_m;
> u32 freq_n;
> +#if defined(CONFIG_HAVE_CLK)
> + struct clk *clk;
> +#endif
> wait_queue_head_t waitq;
> spinlock_t lock;
> struct i2c_msg *msg;
> @@ -521,6 +529,83 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
> 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 int req_freq, const int tclk, int *best_n,
> + int *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)
> +{
> + int bus_freq;
> + int 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 */
> + rc = -ENODEV;
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
> mv64xxx_i2c_probe(struct platform_device *pd)
> {
> @@ -528,7 +613,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
> struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data;
> int rc;
>
> - if ((pd->id != 0) || !pdata)
> + if ((!pdata && !pd->dev.of_node) || (pdata && (pd->id != 0)))
> return -ENODEV;
>
> drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
> @@ -546,19 +631,36 @@ mv64xxx_i2c_probe(struct platform_device *pd)
> init_waitqueue_head(&drv_data->waitq);
> spin_lock_init(&drv_data->lock);
>
> - drv_data->freq_m = pdata->freq_m;
> - drv_data->freq_n = pdata->freq_n;
> - drv_data->irq = platform_get_irq(pd, 0);
> +#if defined(CONFIG_HAVE_CLK)
> + /* Not all platforms have a clk */
> + 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);
> + }
> + if (pd->dev.of_node) {
else if?
> + rc = mv64xxx_of_config(drv_data, pd->dev.of_node);
> + if (rc)
> + goto exit_unmap_regs;
> + }
> if (drv_data->irq < 0) {
> rc = -ENXIO;
> goto exit_unmap_regs;
> }
> +
> drv_data->adapter.dev.parent = &pd->dev;
> drv_data->adapter.algo = &mv64xxx_i2c_algo;
> drv_data->adapter.owner = THIS_MODULE;
> 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.dev.of_node = pd->dev.of_node;
> platform_set_drvdata(pd, drv_data);
> i2c_set_adapdata(&drv_data->adapter, drv_data);
>
> @@ -577,11 +679,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
> goto exit_free_irq;
> }
>
> + of_i2c_register_devices(&drv_data->adapter);
> +
> return 0;
>
> exit_free_irq:
> free_irq(drv_data->irq, drv_data);
> 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);
> exit_kfree:
> kfree(drv_data);
> @@ -597,17 +708,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
> rc = i2c_del_adapter(&drv_data->adapter);
> free_irq(drv_data->irq, 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);
>
> 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 = {
> .probe = mv64xxx_i2c_probe,
> .remove = __devexit_p(mv64xxx_i2c_remove),
> .driver = {
> .owner = THIS_MODULE,
> .name = MV64XXX_I2C_CTLR_NAME,
> + .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
> },
> };
Rest looks good to me and it should be safe to rebase now.
Thanks,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120721/b124e636/attachment.sig>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH] I2C: MV64XYZ: Add Device Tree support
@ 2012-07-22 10:51 Andrew Lunn
2012-07-23 20:03 ` Wolfram Sang
0 siblings, 1 reply; 5+ messages in thread
From: Andrew Lunn @ 2012-07-22 10:51 UTC (permalink / raw)
To: linux-arm-kernel
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: Andrew Lunn <andrew@lunn.ch>
---
- Replaced the timeout property in DT, with a hard coded 1 second.
- Put all the OF code together.
- Split the ARM parts from the driver itself.
- Dropped Sebastian Hesselbarth Acked-by, since the changes are not trivial.
- Change MV64XXX to MV64XYZ in the Subject to try to get it past the list
spam filter.
- return -ENODEV and else if comments from Wolfram.
- Rebased on i2c-embedded/for-next
Documentation/devicetree/bindings/i2c/mrvl-i2c.txt | 19 ++-
drivers/i2c/busses/i2c-mv64xxx.c | 134 +++++++++++++++++++-
2 files changed, 147 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
index b891ee2..0f79450 100644
--- a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
+++ b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt
@@ -1,4 +1,4 @@
-* I2C
+* Marvell MMP I2C controller
Required properties :
@@ -32,3 +32,20 @@ Examples:
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 at 11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ interrupts = <29>;
+ clock-frequency = <100000>;
+ };
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 6e70eea..3d53f4e 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -18,6 +18,11 @@
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.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 */
#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
int rc;
u32 freq_m;
u32 freq_n;
+#if defined(CONFIG_HAVE_CLK)
+ struct clk *clk;
+#endif
wait_queue_head_t waitq;
spinlock_t lock;
struct i2c_msg *msg;
@@ -521,6 +529,83 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
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 int req_freq, const int tclk, int *best_n,
+ int *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)
+{
+ int bus_freq;
+ int 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
mv64xxx_i2c_probe(struct platform_device *pd)
{
@@ -528,7 +613,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data;
int rc;
- if (!pdata)
+ if ((!pdata && !pd->dev.of_node))
return -ENODEV;
drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
@@ -546,19 +631,35 @@ mv64xxx_i2c_probe(struct platform_device *pd)
init_waitqueue_head(&drv_data->waitq);
spin_lock_init(&drv_data->lock);
- drv_data->freq_m = pdata->freq_m;
- drv_data->freq_n = pdata->freq_n;
- drv_data->irq = platform_get_irq(pd, 0);
+#if defined(CONFIG_HAVE_CLK)
+ /* Not all platforms have a clk */
+ 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) {
rc = -ENXIO;
goto exit_unmap_regs;
}
+
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
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.dev.of_node = pd->dev.of_node;
platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
@@ -577,11 +678,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_free_irq;
}
+ of_i2c_register_devices(&drv_data->adapter);
+
return 0;
exit_free_irq:
free_irq(drv_data->irq, drv_data);
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);
exit_kfree:
kfree(drv_data);
@@ -597,17 +707,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
rc = i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, 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);
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 = {
.probe = mv64xxx_i2c_probe,
.remove = __devexit_p(mv64xxx_i2c_remove),
.driver = {
.owner = THIS_MODULE,
.name = MV64XXX_I2C_CTLR_NAME,
+ .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
},
};
--
1.7.10.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH] I2C: MV64XYZ: Add Device Tree support
2012-07-22 10:51 [PATCH] I2C: MV64XYZ: Add Device Tree support Andrew Lunn
@ 2012-07-23 20:03 ` Wolfram Sang
2012-07-24 6:42 ` Andrew Lunn
0 siblings, 1 reply; 5+ messages in thread
From: Wolfram Sang @ 2012-07-23 20:03 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Jul 22, 2012 at 12:51:35PM +0200, Andrew Lunn wrote:
> 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: Andrew Lunn <andrew@lunn.ch>
Sparse complained about this:
drivers/i2c/busses/i2c-mv64xxx.c:584:54: warning: incorrect type in argument 3 (different signedness)
drivers/i2c/busses/i2c-mv64xxx.c:584:54: expected unsigned int [usertype] *out_value
drivers/i2c/busses/i2c-mv64xxx.c:584:54: got int *<noident>
drivers/i2c/busses/i2c-mv64xxx.c:586:41: warning: incorrect type in argument 3 (different signedness)
drivers/i2c/busses/i2c-mv64xxx.c:586:41: expected int *best_n
drivers/i2c/busses/i2c-mv64xxx.c:586:41: got unsigned int *<noident>
drivers/i2c/busses/i2c-mv64xxx.c:586:60: warning: incorrect type in argument 4 (different signedness)
drivers/i2c/busses/i2c-mv64xxx.c:586:60: expected int *best_m
drivers/i2c/busses/i2c-mv64xxx.c:586:60: got unsigned int *<noident>
I applied your patch with the following fix to -next. Let me know if you agree
with that.
Regards,
Wolfram
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -537,8 +537,8 @@ mv64xxx_calc_freq(const int tclk, const int n, const int m)
}
static bool __devinit
-mv64xxx_find_baud_factors(const int req_freq, const int tclk, int *best_n,
- int *best_m)
+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;
@@ -564,8 +564,7 @@ static int __devinit
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
struct device_node *np)
{
- int bus_freq;
- int tclk;
+ u32 bus_freq, tclk;
int rc = 0;
/* CLK is mandatory when using DT to describe the i2c bus. We
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120723/eb671a3b/attachment.sig>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH] I2C: MV64XYZ: Add Device Tree support
2012-07-23 20:03 ` Wolfram Sang
@ 2012-07-24 6:42 ` Andrew Lunn
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Lunn @ 2012-07-24 6:42 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jul 23, 2012 at 10:03:59PM +0200, Wolfram Sang wrote:
> On Sun, Jul 22, 2012 at 12:51:35PM +0200, Andrew Lunn wrote:
> > 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: Andrew Lunn <andrew@lunn.ch>
>
> Sparse complained about this:
>
> drivers/i2c/busses/i2c-mv64xxx.c:584:54: warning: incorrect type in argument 3 (different signedness)
> drivers/i2c/busses/i2c-mv64xxx.c:584:54: expected unsigned int [usertype] *out_value
> drivers/i2c/busses/i2c-mv64xxx.c:584:54: got int *<noident>
> drivers/i2c/busses/i2c-mv64xxx.c:586:41: warning: incorrect type in argument 3 (different signedness)
> drivers/i2c/busses/i2c-mv64xxx.c:586:41: expected int *best_n
> drivers/i2c/busses/i2c-mv64xxx.c:586:41: got unsigned int *<noident>
> drivers/i2c/busses/i2c-mv64xxx.c:586:60: warning: incorrect type in argument 4 (different signedness)
> drivers/i2c/busses/i2c-mv64xxx.c:586:60: expected int *best_m
> drivers/i2c/busses/i2c-mv64xxx.c:586:60: got unsigned int *<noident>
>
> I applied your patch with the following fix to -next. Let me know if you agree
> with that.
Hi Wolfram
Looks good, thanks for the fix.
Andrew
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2012-07-24 6:42 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-22 10:51 [PATCH] I2C: MV64XYZ: Add Device Tree support Andrew Lunn
2012-07-23 20:03 ` Wolfram Sang
2012-07-24 6:42 ` Andrew Lunn
-- strict thread matches above, loose matches on Subject: below --
2012-07-20 17:00 Andrew Lunn
2012-07-21 12:55 ` Wolfram Sang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).