On 09/11/2013 03:38 AM, Graeme Gregory wrote:
On Fri, Sep 06, 2013 at 10:59:31AM -0600, al.stone@linaro.org wrote:
From: Al Stone ahs3@redhat.com
This code allows the Samsung pinctrl driver to use either FDT or ACPI in defining pin controllers (i.e., collections of GPIO controllers, in this case). On probe, the driver first attempts to configure a driver using FDT; failing that, using ACPI is attempted. The size of the patch is due to essentially having to duplicate the FDT paths for ACPI, since the FDT code made assumptions about data structures that are not necessarily available with ACPI.
Signed-off-by: Al Stone al.stone@linaro.org
drivers/pinctrl/pinctrl-samsung.c | 503 +++++++++++++++++++++++++++++++++++++- drivers/pinctrl/pinctrl-samsung.h | 3 + 2 files changed, 499 insertions(+), 7 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index a7fa9e2..422e632 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -29,6 +29,7 @@ #include <linux/irqdomain.h> #include <linux/spinlock.h> #include <linux/syscore_ops.h> +#include <linux/acpi.h>
#include "core.h" #include "pinctrl-samsung.h" @@ -716,12 +717,477 @@ static int samsung_pinctrl_parse_dt(struct platform_device *pdev, return 0; }
+/* parse the acpi resources for this pinctrl bank */ +static acpi_status samsung_pinctrl_bank_resource(struct acpi_resource *resource,
void *context)
+{
- struct samsung_pinctrl_drv_data *drvdata = context;
- struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
- struct device *dev = drvdata->dev;
- struct samsung_pin_bank *bank = ctrl->pin_banks;
- switch (resource->type) {
- case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: {
struct acpi_resource_fixed_memory32 *p =
&resource->data.fixed_memory32;
bank->pctl_offset = p->address;
return AE_OK;
- }
- case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
- default:
dev_info(dev, "Resource %d is not an MMIO resources\n",
resource->type);
- }
- return AE_CTRL_TERMINATE;
+}
+/* Handle pin bank info when a device is found */ +static acpi_status samsung_acpi_read_banks(acpi_handle handle, u32 level,
void *data, void **return_value)
+{
- acpi_status status;
- struct samsung_pinctrl_drv_data *drvdata = data;
- struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
- struct samsung_pin_bank *bank = ctrl->pin_banks;
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
- char *tag;
- u64 value;
- int ii;
- u32 cnt, len;
- /*
* See if there is a TAG. If so, we're still interested -- this
* is what tells us it is a pin bank
*/
- status = acpi_evaluate_object(handle, "TAG", NULL, &buf);
- if (ACPI_FAILURE(status)) {
ACPI_FREE(buf.pointer);
return AE_OK; /* the device is irrelevant */
- }
- cnt = *(u32 *)buf.pointer;
- len = *(u32 *)(buf.pointer + sizeof(u32));
- if (cnt != 2 || len < 1) {
/*
* The object is supposed to have 2 fields, and
* the string length is supposed to > zero
*/
ACPI_FREE(buf.pointer);
return AE_BAD_PARAMETER;
- }
- /*
* We know that the first 4 bytes is the number of fields,
* the second four bytes is the string length. The next 8
* bytes are the address that points to the beginning of
* the string in the buffer -- which is always immediately
* after the 8 bytes, in this case. So, just set our
* pointer to the right value and be done with it.
*/
- tag = (char *)devm_kzalloc(drvdata->dev, len + 1, GFP_KERNEL);
- if (!tag) {
ACPI_FREE(buf.pointer);
return AE_NO_MEMORY;
- }
- memcpy(tag, (char *)(buf.pointer + 16), len);
- /* find first available pin bank for this controller */
- for (ii = 0; ii < ctrl->nr_banks; ii++, bank++)
if (bank->type == NULL)
break;
- if (ii >= ctrl->nr_banks) {
ACPI_FREE(buf.pointer);
devm_kfree(drvdata->dev, tag);
return AE_BUFFER_OVERFLOW;
- }
- /* initialize the bank */
- status = acpi_walk_resources(handle, METHOD_NAME__CRS,
samsung_pinctrl_bank_resource, drvdata);
- if (ACPI_FAILURE(status)) {
ACPI_FREE(buf.pointer);
devm_kfree(drvdata->dev, tag);
return status;
- }
- bank->type = devm_kzalloc(drvdata->dev,
sizeof(*(bank->type)),
GFP_KERNEL);
- if (!(bank->type)) {
ACPI_FREE(buf.pointer);
devm_kfree(drvdata->dev, tag);
return AE_NO_MEMORY;
- }
- status = acpi_evaluate_integer(handle, "BASE", NULL, &value);
- if (ACPI_FAILURE(status)) {
/* but, we should have BASE if we have the TAG */
ACPI_FREE(buf.pointer);
devm_kfree(drvdata->dev, tag);
devm_kfree(drvdata->dev, bank->type);
return status;
- }
These error paths are quite tedious, might be better to jump to a label that performs these actions at the end of the function rather than repeat this over and over.
Yeah, fair enough. I personally go back and forth on this. Part of it is just having it pounded into me early on that "goto's are evil"; I'm still recovering.
I'll rearrange this.
Also does this actually need devm_kfree()? If probe fails becuase of this then the memory is de-allocated for us.
Aha. I don't know why I was unaware of that. If that's the case, then I'll simplify based on that.
- bank->pin_base = (u32)value + ctrl->nr_pins;
- status = acpi_evaluate_integer(handle, "NPIN", NULL, &value);
- if (ACPI_FAILURE(status)) {
/* but, we should have NPIN if we have the PINS method */
ACPI_FREE(buf.pointer);
devm_kfree(drvdata->dev, tag);
devm_kfree(drvdata->dev, bank->type);
return status;
- }
- bank->nr_pins = (u32)value;
- ctrl->nr_pins += bank->nr_pins;
- bank->gpio_chip.base = pin_base;
- pin_base += bank->nr_pins;
- bank->name = tag;
- bank->acpi_node = &drvdata->dev->acpi_node;
- bank->drvdata = drvdata;
- spin_lock_init(&bank->slock);
- ACPI_FREE(buf.pointer);
- return AE_OK;
+}
+/* initialize the samsung_pin_ctrl portion of the device data */ +static int samsung_pin_ctrl_acpi_parse(
struct samsung_pinctrl_drv_data *drvdata,
struct platform_device *pdev)
+{
- acpi_status status;
- struct acpi_handle *dev_handle = ACPI_HANDLE(&pdev->dev);
- struct samsung_pin_ctrl *ctrl = NULL;
- struct device *dev = &pdev->dev;
- int nbanks;
- struct samsung_pin_bank *banks;
- u64 value;
- drvdata->dev = dev;
- ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
- if (!ctrl) {
dev_err(dev, "cannot allocate memory for samsung_pin_ctrl\n");
return -ENOMEM;
- }
- drvdata->ctrl = ctrl;
- status = acpi_evaluate_integer(dev_handle, "BNKS", NULL, &value);
- if (ACPI_FAILURE(status)) {
devm_kfree(dev, ctrl);
dev_err(dev, "cannot get BNKS value\n");
return -EINVAL;
- }
- nbanks = (int)value;
- banks = devm_kzalloc(dev, nbanks * sizeof(*banks), GFP_KERNEL);
- if (!banks) {
devm_kfree(dev, ctrl);
dev_err(dev, "cannot allocate memory for pin banks list\n");
return -ENOMEM;
- }
- ctrl->pin_banks = banks;
- ctrl->nr_banks = nbanks;
- ctrl->base = pin_base;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, dev_handle, 1,
samsung_acpi_read_banks, NULL,
drvdata, NULL);
- if (ACPI_FAILURE(status)) {
devm_kfree(dev, banks);
devm_kfree(dev, ctrl);
dev_err(dev, "failure walking controller for banks\n");
return -EINVAL;
- }
- return 0;
+}
+/* Handle pin group info when a device is found */ +static acpi_status samsung_acpi_read_group(acpi_handle handle, u32 level,
void *data, void **return_value)
+{
- acpi_status status;
- struct samsung_pinctrl_drv_data *drvdata = data;
- struct pinctrl_desc *pcdesc = &drvdata->pctl;
- struct device *dev = drvdata->dev;
- struct samsung_pin_group *grp;
- struct samsung_pmx_func *pmx;
- u64 value;
- unsigned int *pins;
- unsigned int npins;
- struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_buffer format;
- struct acpi_buffer info = { ACPI_ALLOCATE_BUFFER, NULL };
- int ii, len;
- u8 func;
- char **pp, *pname, *pname2;
- const int BUFSIZ = 256;
- char buffer[BUFSIZ];
- struct acpi_buffer name_buffer = { BUFSIZ, &buffer };
- /* see if there is an available pin group...there better be */
- grp = (struct samsung_pin_group *)&drvdata->pin_groups[0];
- for (ii = 0; ii < drvdata->nr_groups; ii++, grp++) {
if (grp->num_pins == 0)
break;
- }
- if (!grp && ii >= drvdata->nr_groups) {
dev_err(dev, "too many pin groups defined for %s \n",
(char *)name_buffer.pointer);
return AE_BAD_PARAMETER;
- }
- /* see if there is a pin list to get -- no method == not a pin group */
- status = acpi_evaluate_object(handle, "PINS", NULL, &buf);
- if (ACPI_FAILURE(status)) {
ACPI_FREE(buf.pointer);
return AE_OK; /* the device is irrelevant */
- }
- status = acpi_evaluate_integer(handle, "NPIN", NULL, &value);
- if (ACPI_FAILURE(status)) {
/* but, we should have NPIN if we have the PINS method */
ACPI_FREE(buf.pointer);
return status;
- }
- npins = (unsigned int)value;
- grp->num_pins = npins;
- /* find the function number to use for this group */
- status = acpi_evaluate_integer(handle, "FUNC", NULL, &value);
- if (ACPI_FAILURE(status)) {
/* we must have a function number */
ACPI_FREE(buf.pointer);
return status;
- }
- func = (u8)value;
- grp->func = func;
- /* find the name to use for this group */
- memset(buffer, 0, BUFSIZ);
- status = acpi_get_name(handle, ACPI_SINGLE_NAME, &name_buffer);
- if (ACPI_FAILURE(status)) {
ACPI_FREE(buf.pointer);
return status;
- }
- len = strlen(name_buffer.pointer);
- pname = devm_kzalloc(dev, len + GSUFFIX_LEN + 1, GFP_KERNEL);
- if (!pname) {
dev_err(dev, "cannot alloc group name space for %s \n",
(char *)name_buffer.pointer);
ACPI_FREE(buf.pointer);
status = AE_NO_MEMORY;
- }
- memcpy(pname, name_buffer.pointer, len);
- memcpy(pname + len, GROUP_SUFFIX, GSUFFIX_LEN);
- grp->name = pname;
- /* allocate the space needed for the pin list for this group */
- pins = devm_kzalloc(dev, npins * sizeof(*(pcdesc->pins)), GFP_KERNEL);
- if (!pins) {
dev_err(dev, "cannot alloc group pin list space for %s \n",
(char *)name_buffer.pointer);
ACPI_FREE(buf.pointer);
devm_kfree(dev, pname);
return AE_NO_MEMORY;
- }
- /* extract the strings from the package provided by the PINS method */
- format.pointer = kzalloc((npins * sizeof(char)) + 1, GFP_KERNEL);
- if (!format.pointer) {
ACPI_FREE(buf.pointer);
devm_kfree(dev, pname);
return AE_NO_MEMORY;
- }
- format.length = npins + 1;
- memset(format.pointer, 'S', npins);
- status = acpi_extract_package(buf.pointer, &format, &info);
- if (ACPI_FAILURE(status)) {
dev_err(dev, "cannot parse pin names for %s\n",
(char *)name_buffer.pointer);
ACPI_FREE(buf.pointer);
ACPI_FREE(info.pointer);
devm_kfree(dev, pname);
kfree(format.pointer);
return status;
- }
- kfree(format.pointer); /* match the kzalloc() above (vs ACPI_FREE) */
- ACPI_FREE(buf.pointer);
- ACPI_FREE(info.pointer);
- /* set up the group pins */
- pp = (char **)info.pointer;
- for (ii = 0; ii < grp->num_pins; ii++, pp++) {
pins[ii] = ii;
- }
- grp->pins = pins;
- /* set up the functions */
- len = strlen((char *)name_buffer.pointer);
- pname2 = devm_kzalloc(dev, len + FSUFFIX_LEN + 1, GFP_KERNEL);
- if (!pname2) {
dev_err(dev, "cannot alloc function name space for %s \n",
(char *)name_buffer.pointer);
devm_kfree(dev, pname);
return AE_NO_MEMORY;
- }
- memcpy(pname2, (char *)name_buffer.pointer, len);
- memcpy(pname2 + len, FUNCTION_SUFFIX, FSUFFIX_LEN);
- pmx = (struct samsung_pmx_func *)drvdata->pmx_functions;
- for (ii = 0; ii < drvdata->nr_functions; ii++, pmx++) {
if (pmx->name)
if (strncmp(pmx->name, pname2, strlen(pname2)) == 0)
break;
- }
- if (ii >= drvdata->nr_functions) {
pmx = (struct samsung_pmx_func *)drvdata->pmx_functions;
for (ii = 0; ii < drvdata->nr_functions; ii++, pmx++)
if (!pmx->name)
break;
- }
- if (ii >= drvdata->nr_functions) {
dev_err(dev, "too many pin functions defined for %s \n",
(char *)name_buffer.pointer);
devm_kfree(dev, pname);
devm_kfree(dev, pname2);
return AE_BAD_PARAMETER;
- }
- pmx->name = pname2;
- return AE_OK;
+}
+/*
- Parse all of the pin group and function information available
- for this pin controller. A pin group is formed with all
- the pins returned by evaluating any PINS method found in any
- child node.
- */
+static int samsung_pin_group_acpi_parse(
struct samsung_pinctrl_drv_data *drvdata,
struct platform_device *pdev)
+{
- acpi_status status;
- struct acpi_handle *dev_handle = ACPI_HANDLE(&pdev->dev);
- struct device *dev = &pdev->dev;
- u64 value;
- int gcnt, fcnt;
- struct samsung_pin_group *grps;
- struct samsung_pmx_func *funcs;
- status = acpi_evaluate_integer(dev_handle, "NGRP", NULL, &value);
- if (ACPI_FAILURE(status)) {
dev_err(dev, "cannot get NGRP value\n");
return -EINVAL;
- }
- gcnt = (int)value;
- fcnt = gcnt;
- grps = devm_kzalloc(dev, gcnt * sizeof(*grps), GFP_KERNEL);
- if (!grps) {
dev_err(dev, "cannot allocate memory for pin group list\n");
return -EINVAL;
- }
- funcs = devm_kzalloc(dev, fcnt * sizeof(*funcs), GFP_KERNEL);
- if (!funcs) {
devm_kfree(dev, grps);
dev_err(dev, "cannot allocate memory for function list\n");
return -EINVAL;
- }
- drvdata->dev = dev;
- drvdata->pin_groups = grps;
- drvdata->nr_groups = gcnt;
- drvdata->pmx_functions = funcs;
- drvdata->nr_functions = fcnt;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, dev_handle, 1,
samsung_acpi_read_group, NULL,
drvdata, NULL);
- if (ACPI_FAILURE(status)) {
devm_kfree(dev, grps);
devm_kfree(dev, funcs);
dev_err(dev, "failure walking controller for groups\n");
return -EINVAL;
- }
- return 0;
+}
+/* parse the acpi resources for this pin controller */ +static acpi_status samsung_pinctrl_resource(struct acpi_resource *resource,
void *context)
+{
- struct samsung_pinctrl_drv_data *drvdata = context;
- struct device *dev = drvdata->dev;
- switch (resource->type) {
- case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: {
struct acpi_resource_fixed_memory32 *p =
&resource->data.fixed_memory32;
drvdata->virt_base = (void *)p->address;
return AE_OK;
- }
- case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
- default:
dev_info(dev, "Resource %d is not an MMIO resources\n",
resource->type);
- }
- return AE_CTRL_TERMINATE;
+}
+/* parse the ACPI tables */ +static const struct acpi_device_id samsung_pinctrl_acpi_match[];
+static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_acpi_data(
struct samsung_pinctrl_drv_data *drvdata,
struct platform_device *pdev)
+{
- acpi_status status;
- struct acpi_handle *dev_handle = ACPI_HANDLE(&pdev->dev);
- struct samsung_pin_ctrl *ctrl = NULL;
- struct device *dev = &pdev->dev;
- int ret;
- if (!dev_handle) {
dev_err(&pdev->dev, "cannot get ACPI data with null handle\n");
return NULL;
- }
- /* where are the pin controller registers? */
- status = acpi_walk_resources(dev_handle, METHOD_NAME__CRS,
samsung_pinctrl_resource, drvdata);
- if (ACPI_FAILURE(status)) {
dev_err(dev, "registers in _CRS are required\n");
return NULL;
- }
- /* initialize the samsung_pin_ctrl part of samsung_pinctrl_drv_data */
- ret = samsung_pin_ctrl_acpi_parse(drvdata, pdev);
- if (ret)
return NULL;
- /* initialize the samsung_pin_group part of samsung_pinctrl_drv_data */
- ret = samsung_pin_group_acpi_parse(drvdata, pdev);
- if (ret)
ctrl = NULL;
- else
ctrl = drvdata->ctrl;
- return ctrl;
+}
- /* register the pinctrl interface with the pinctrl subsystem */ static int samsung_pinctrl_register(struct platform_device *pdev, struct samsung_pinctrl_drv_data *drvdata) { struct pinctrl_desc *ctrldesc = &drvdata->pctl; struct pinctrl_pin_desc *pindesc, *pdesc;
- struct acpi_handle *dev_handle; struct samsung_pin_bank *pin_bank; char *pin_names; int pin, bank, ret;
@@ -784,7 +1250,10 @@ static int samsung_pinctrl_register(struct platform_device *pdev, pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange); }
- ret = samsung_pinctrl_parse_dt(pdev, drvdata);
- dev_handle = ACPI_HANDLE(&pdev->dev);
- ret = 0;
- if (pdev->dev.of_node && !dev_handle)
if (ret) { pinctrl_unregister(drvdata->pctl_dev); return ret;ret = samsung_pinctrl_parse_dt(pdev, drvdata);
@@ -828,6 +1297,9 @@ static int samsung_gpiolib_register(struct platform_device *pdev, gc->label, ret); goto fail; }
printk(KERN_DEBUG
"samsung-pinctrl: registered gpio %s, pins %d to %d\n",
gc->label, gc->base, gc->base + gc->ngpio - 1);
}
return 0;
@@ -861,7 +1333,7 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev, static const struct of_device_id samsung_pinctrl_dt_match[];
/* retrieve the soc specific data */ -static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( +static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_of_data( struct samsung_pinctrl_drv_data *d, struct platform_device *pdev) { @@ -875,7 +1347,7 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
id = of_alias_get_id(node, "pinctrl"); if (id < 0) {
dev_err(&pdev->dev, "failed to get alias id\n");
return NULL; } match = of_match_node(samsung_pinctrl_dt_match, node);/* this node may be in ACPI so this may not be an error yet */
@@ -913,10 +1385,13 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct samsung_pin_ctrl *ctrl; struct resource *res;
- struct acpi_handle *dev_handle; int ret;
- if (!dev->of_node) {
dev_err(dev, "device tree node not found\n");
- dev_handle = DEVICE_ACPI_HANDLE(dev);
- if (!pdev->dev.of_node && !dev_handle) {
return -ENODEV; }dev_err(dev, "no device info available\n");
@@ -927,9 +1402,12 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; }
- ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);
- ctrl = samsung_pinctrl_get_soc_of_data(drvdata, pdev);
- if (!ctrl) { /* if not device tree, try ACPI */
ctrl = samsung_pinctrl_get_soc_acpi_data(drvdata, pdev);
- } if (!ctrl) {
dev_err(&pdev->dev, "driver data not available\n");
return -EINVAL; } drvdata->ctrl = ctrl;dev_err(&pdev->dev, "cannot retrieve device details\n");
@@ -1134,12 +1612,23 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match);
+#ifdef CONFIG_ACPI +static const struct acpi_device_id samsung_pinctrl_acpi_match[] = {
- { "LINA0002", },
- {},
+}; +MODULE_DEVICE_TABLE(acpi, samsung_pinctrl_acpi_match); +#endif
- static struct platform_driver samsung_pinctrl_driver = { .probe = samsung_pinctrl_probe, .driver = { .name = "samsung-pinctrl", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_pinctrl_dt_match),
+#ifdef CONFIG_ACPI
.acpi_match_table = ACPI_PTR(samsung_pinctrl_acpi_match),
+#endif }, };
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 11bb75b..cfbb408 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -141,6 +141,9 @@ struct samsung_pin_bank { char *name; void *soc_priv; struct device_node *of_node; +#ifdef CONFIG_ACPI
- struct acpi_dev_node *acpi_node;
+#endif struct samsung_pinctrl_drv_data *drvdata; struct irq_domain *irq_domain; struct gpio_chip gpio_chip; -- 1.8.3.1
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi