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), }, };