Graeme,
very nice patch, very clear, it turns out to be also the first excellent example documenting how to add ACPI support alongside FDT support for all developers! Shall we copy-paste few excerpts into the ACPI wiki page maybe?
I was about to ask one silly question of mine in the code below but found the answer myself by reading the code itself!
thanks!
Andrea
On 27 May 2013 17:40, Graeme Gregory graeme.gregory@linaro.org wrote:
From: Graeme Gregory graeme.gregory@linaro.org
Add prototype ACPI probing for this driver. It fetches resources required by probe from the ACPI tables.
Signed-off-by: Graeme Gregory graeme.gregory@linaro.org
drivers/i2c/busses/i2c-s3c2410.c | 167 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 580199c..845a2fe 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -38,6 +38,7 @@ #include <linux/io.h> #include <linux/of_i2c.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/pinctrl/consumer.h>
#include <asm/irq.h> @@ -123,6 +124,14 @@ static const struct of_device_id s3c24xx_i2c_match[] = { MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); #endif
+#ifdef CONFIG_ACPI +static const struct acpi_device_id s3c24xx_i2c_acpi_match[] = {
{ "LINA0001", },
{ }
+}; +MODULE_DEVICE_TABLE(acpi, s3c24xx_i2c_acpi_match); +#endif
/* s3c24xx_get_device_quirks
- Get controller type either from device tree or platform device variant.
@@ -130,12 +139,22 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pdev) {
struct acpi_handle *dev_handle;
struct acpi_device_id const *dev_id;
dev_handle = ACPI_HANDLE(&pdev->dev);
if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); return (unsigned int)match->data; }
if (dev_handle) {
dev_id = acpi_match_device(s3c24xx_i2c_acpi_match, &pdev->dev);
return (unsigned int)dev_id->driver_data;
}
return platform_get_device_id(pdev)->driver_data;
}
@@ -946,6 +965,107 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c) } #endif
+#ifdef CONFIG_ACPI +/*
- ACPI resource callback
- */
+static acpi_status +s3c24xx_i2c_acpi_resource(struct acpi_resource *resource, void *context) +{
u32 i;
struct s3c24xx_i2c *i2c = context;
switch (resource->type) {
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
/* IRQ resources have already been read */
return AE_OK;
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
/* Mem resources have already been read */
return AE_OK;
case ACPI_RESOURCE_TYPE_GPIO:
{
struct acpi_resource_gpio *p = &resource->data.gpio;
if (p->pin_table_length != 2) {
dev_err(i2c->dev, "2 GPIOS required in resource\n");
return AE_OK;
}
for (i = 0; i < p->pin_table_length; i++)
i2c->gpios[i] = p->pin_table[i];
return AE_OK;
}
default:
dev_info(i2c->dev, "Resource %d isn't MMIO, IRQ or GPIO\n",
resource->type);
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
}
return AE_CTRL_TERMINATE;
+}
+static int s3c24xx_i2c_parse_acpi_gpio(struct s3c24xx_i2c *i2c) +{
int idx, gpio, ret;
struct acpi_handle *dev_handle = ACPI_HANDLE(i2c->dev);
if (i2c->quirks & QUIRK_NO_GPIO)
return 0;
ret = acpi_walk_resources(dev_handle, METHOD_NAME__CRS,
s3c24xx_i2c_acpi_resource, i2c);
if (ACPI_FAILURE(ret)) {
pr_warn("Failure evaluating %s\n", METHOD_NAME__CRS);
return -EINVAL;
}
for (idx = 0; idx < 2; idx++) {
gpio = i2c->gpios[idx];
if (!gpio_is_valid(gpio)) {
dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
goto free_gpio;
}
i2c->gpios[idx] = gpio;
ret = gpio_request(gpio, "i2c-bus");
if (ret) {
dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
goto free_gpio;
}
}
return 0;
+free_gpio:
while (--idx >= 0)
gpio_free(i2c->gpios[idx]);
return -EINVAL;
+}
+static void s3c24xx_i2c_acpi_gpio_free(struct s3c24xx_i2c *i2c) +{
unsigned int idx;
if (i2c->quirks & QUIRK_NO_GPIO)
return;
for (idx = 0; idx < 2; idx++)
gpio_free(i2c->gpios[idx]);
+} +#else +static int s3c24xx_i2c_parse_acpi_gpio(struct s3c24xx_i2c *i2c) +{
return 0;
+}
+static void s3c24xx_i2c_acpi_gpio_free(struct s3c24xx_i2c *i2c) +{ +} +#endif
/* s3c24xx_i2c_init
- initialise the controller, set the IO lines and frequency
@@ -1013,6 +1133,27 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) } #endif
+#ifdef CONFIG_ACPI +/* s3c24xx_i2c_parse_acpi
- Parse the device tree node and retreive the platform data.
+*/
+static void +s3c24xx_i2c_parse_acpi(struct device *device, struct s3c24xx_i2c *i2c) +{
struct s3c2410_platform_i2c *pdata = i2c->pdata;
pdata->bus_num = -1; /* i2c bus number is dynamically assigned */
+} +#else +static void +s3c24xx_i2c_parse_acpi(struct device_node *np, struct s3c24xx_i2c *i2c) +{
return;
+} +#endif
/* s3c24xx_i2c_probe
- called by the bus driver when a suitable device is found
@@ -1023,9 +1164,12 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) struct s3c24xx_i2c *i2c; struct s3c2410_platform_i2c *pdata = NULL; struct resource *res;
struct acpi_handle *dev_handle; int ret;
if (!pdev->dev.of_node) {
dev_handle = DEVICE_ACPI_HANDLE(&pdev->dev);
if (!pdev->dev.of_node && !dev_handle) { pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data\n");
@@ -1046,10 +1190,14 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) }
i2c->quirks = s3c24xx_get_device_quirks(pdev);
if (pdata)
if (pdata) { memcpy(i2c->pdata, pdata, sizeof(*pdata));
else
s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
} else {
if (pdev->dev.of_node)
s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
if (dev_handle)
s3c24xx_i2c_parse_acpi(&pdev->dev, i2c);
} strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE;
@@ -1071,7 +1219,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
/* map the registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1099,6 +1246,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
if (i2c->pdata->cfg_gpio) { i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
} else if (dev_handle) {
s3c24xx_i2c_parse_acpi_gpio(i2c); } else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) { return -EINVAL; }
@@ -1172,6 +1321,9 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
struct acpi_handle *dev_handle;
dev_handle = DEVICE_ACPI_HANDLE(&pdev->dev); pm_runtime_disable(&i2c->adap.dev); pm_runtime_disable(&pdev->dev);
@@ -1182,9 +1334,13 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
clk_disable_unprepare(i2c->clk);
if (pdev->dev.of_node && IS_ERR(i2c->pctrl)) s3c24xx_i2c_dt_gpio_free(i2c);
if (dev_handle)
s3c24xx_i2c_acpi_gpio_free(i2c);
return 0;
}
@@ -1237,6 +1393,7 @@ static struct platform_driver s3c24xx_i2c_driver = { .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, .of_match_table = of_match_ptr(s3c24xx_i2c_match),
.acpi_match_table = ACPI_PTR(s3c24xx_i2c_acpi_match), },
};
-- 1.7.10.4
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
-- Andrea Gallo Director, Linaro Enterprise Group email: andrea.gallo@linaro.org mobile: +39 338 4075993 IRC: agallo@#linaro on irc.linaro.org Skype: agallo70