diff options
author | Braden Kell <bradenkell@google.com> | 2017-07-07 17:09:37 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-07-07 17:09:37 +0000 |
commit | 28c03694f5a431908cb53ffa8a65f584d1d978d1 (patch) | |
tree | 58f252d637c1229d9a8802d9842a33a9e62afcd1 | |
parent | 3a4c2840f069dbb858e62fe9c611b60f20c1f8f9 (diff) | |
parent | 56d8b75892c409e0eeeaa473df59460e7abda062 (diff) | |
download | pi-v4.4-28c03694f5a431908cb53ffa8a65f584d1d978d1.tar.gz |
Merge changes from topic 'rtpinconfig-console' into nyc-iot-dev
am: 56d8b75892
Change-Id: I784ff9276a2337a1d18c04aa338f41a971b19757
-rw-r--r-- | drivers/pinctrl/android-things/devices.c | 288 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/devicetree.c | 1 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/main.c | 21 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/platform_devices.c | 13 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/platform_devices.h | 11 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/runtimepinconfig.h | 6 |
6 files changed, 235 insertions, 105 deletions
diff --git a/drivers/pinctrl/android-things/devices.c b/drivers/pinctrl/android-things/devices.c index 2c5fa8bcf99c..6ba8905ce1c8 100644 --- a/drivers/pinctrl/android-things/devices.c +++ b/drivers/pinctrl/android-things/devices.c @@ -21,25 +21,28 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/printk.h> #include <linux/slab.h> -#include "runtimepinconfig.h" #include "platform_devices.h" +#include "runtimepinconfig.h" + +#define MAX_PIN_DIGITS 2 DEFINE_MUTEX(sysfs_mutex); -static struct pin_device **pin_array; +static struct pin_device *pin_array; static inline struct pin_device *get_pin_device_by_pin(u32 pin) { - if (!pin_array || pin >= pin_count) + if (!pin_array || pin > pin_max || pin < pin_min) return NULL; - return pin_array[pin]; + return &pin_array[pin - pin_min]; } /* * Call a function for each pin in a device tree node. Look up the pin_device - * represnting the pin and pass that to the function. Don't call the function if + * representing the pin and pass that to the function. Don't call the function if * no pin_device is found for a pin. */ static int device_for_each_pin(struct device_node *node, @@ -79,22 +82,16 @@ static int device_for_each_pin(struct device_node *node, } /* - * track_pin_device() - Create a pin_device for a platform_device that has just - * been registered, or update it if one already exists. This function should be - * called from the driver's probe function. + * track_pin_device() - Update a pin_device for a platform_device that has just + * been registered. This function should be called from the driver's probe + * function. * * @dev: The platform_device for this pin. - * @class: The class to use when creating sysfs files for this device. For - * example, using the pinctrl class causes files to be created in - * /sys/class/pinctrl. * - * Returns the new (or updated) pin_device on success, NULL on failure. + * Returns the updated pin_device on success, NULL on failure. */ -struct pin_device *track_pin_device(struct platform_device *dev, - struct class *class) +struct pin_device *track_pin_device(struct platform_device *dev) { - const char *name; - struct device *chardev; struct pin_device *pin_dev; int ret; u32 pin; @@ -104,26 +101,12 @@ struct pin_device *track_pin_device(struct platform_device *dev, return NULL; } - if (of_property_read_string(dev->dev.of_node, "name", &name)) { - dev_err(&dev->dev, "no name property found\n"); - return NULL; - } - if ((ret = get_pin(dev->dev.of_node, &pin))) { dev_err(&dev->dev, "no pin property found\n"); return NULL; } - if (pin >= pin_count) { - dev_err(&dev->dev, "pin property %d out of range %d\n", pin, - pin_count); - return NULL; - } - - /* This pin device may be returning after having been unregistered. */ if ((pin_dev = get_pin_device_by_pin(pin))) { - dev_dbg(&dev->dev, "tracking existing device\n"); - if (pin_dev->device != NULL) dev_warn(&dev->dev, "device is being re-added to pin device %d\n", pin); @@ -134,28 +117,8 @@ struct pin_device *track_pin_device(struct platform_device *dev, return pin_dev; } - if (!(pin_dev = kmalloc(sizeof(*pin_dev), GFP_KERNEL))) { - pr_err(TAG "kmalloc failed %s:%d\n", __FILE__, __LINE__); - return NULL; - } - - chardev = device_create(class, NULL, MKDEV(0, 0), pin_dev, "%s", name); - if (IS_ERR(chardev)) { - dev_err(&dev->dev, "unable to create char device"); - kfree(pin_dev); - return NULL; - } - - pin_dev->pin = pin; - pin_dev->set_gpio = 0; - pin_dev->device = dev; - pin_dev->of_node = of_node_get(dev->dev.of_node); - pin_dev->char_device = chardev; - pin_array[pin] = pin_dev; - - dev_dbg(&dev->dev, "tracking new device\n"); - - return pin_dev; + pr_warn(TAG "no pin device found for %d\n", pin); + return NULL; } /* @@ -320,25 +283,105 @@ static inline int register_default_device(struct bcm_device *dev) return register_device_and_aux(dev); } +static int register_pin_device(struct pin_device *dev) +{ + struct property *prop; + struct of_changeset changeset; + int ret; + + if (dev->device) + return 0; + + if ((prop = of_find_property(dev->of_node, "status", NULL))) { + /* + * This pin device marked disabled, meaning it won't be + * registered by register_device_by_node. Instead, we can just + * remove the status property and let the dynamic devicetree + * subsystem handle registration. + */ + of_changeset_init(&changeset); + + if ((ret = of_changeset_remove_property(&changeset, + dev->of_node, prop))) + return ret; + + ret = of_changeset_apply(&changeset); + of_changeset_destroy(&changeset); + return ret; + } else { + return register_device_by_node(dev->of_node); + } +} + +static int set_gpio_flag(struct pin_device *pin) +{ + pin->set_gpio = 1; + return 0; +} + +static int clear_gpio_flag(struct pin_device *pin) +{ + pin->set_gpio = 0; + return 0; +} + /* - * Unregister all peripheral devices we know about and prepare their device tree - * properties for our use. + * Create the pin device array and sysfs character devices, and standardize the + * devicetree configurations of the platform devices. */ -int unregister_platform_devices(void) +int platform_devices_init(struct class *class) { struct device *dev; + struct pin_device *pin_dev; struct bcm_device *bdev; + struct device_node *node; + char *pathbuf; + size_t prefix_len; + u32 i; int ret = 0; if (pin_array) { pr_warn(TAG "pin array is already initialized\n"); } else { - pin_array = kcalloc(pin_count, sizeof(*pin_array), + pin_array = kcalloc(pin_max - pin_min + 1, sizeof(*pin_array), GFP_KERNEL); - if (!pin_array) { - pr_err(TAG "kmalloc failed %s:%d\n", __FILE__, - __LINE__); + if (!pin_array) return -ENOMEM; + } + + prefix_len = strlen(pin_path_prefix); + if (!(pathbuf = kmalloc(prefix_len + MAX_PIN_DIGITS + 1, GFP_KERNEL))) { + ret = -ENOMEM; + goto err_init_path; + } + + strncpy(pathbuf, pin_path_prefix, prefix_len); + + for (i = pin_min; i <= pin_max; i++) { + snprintf(pathbuf + prefix_len, MAX_PIN_DIGITS + 1, "%u", i); + if (!(node = of_find_node_by_path(pathbuf))) { + pr_warn(TAG "can't find node %s\n", pathbuf); + continue; + } + + pin_dev = get_pin_device_by_pin(i); + pin_dev->pin = i; + /* + * Mark this pin as GPIO for now. Later we will unmark pins used + * by registered devices. + */ + pin_dev->set_gpio = 1; + pin_dev->device = NULL; + pin_dev->of_node = node; + pin_dev->char_device = device_create(class, NULL, MKDEV(0, 0), + pin_dev, "%s%u", + pin_prefix, i); + if (IS_ERR(pin_dev->char_device)) { + pin_dev->char_device = NULL; + pr_err(TAG "unable to create char device for pin %u", + i); + ret = -EBADF; + goto err_init_chardev; } } @@ -354,34 +397,115 @@ int unregister_platform_devices(void) continue; } - if (bdev->aux_dev.path) + if (bdev->aux_dev.path) { bdev->aux_dev.of_node = of_find_node_by_path( bdev->aux_dev.path); + if (!bdev->aux_dev.of_node) + pr_warn(TAG "unable to find %s in the device tree\n", + bdev->aux_dev.path); + } - /* Unregister the device and auxiliary device. */ - if (!bdev->use_default) { + if (bdev->init_unreg) { dev = find_device_by_node(bdev->node.of_node); unregister_device_and_aux(dev, bdev); } if ((ret = expand_property(bdev, PROP_PULL, 0))) - return ret; + goto err_init_prop; if ((ret = expand_property(bdev, PROP_FUNC, bdev->pin_groups[0].function))) - return ret; + goto err_init_prop; - if (!bdev->use_default) - ret = set_device_config(bdev, &bdev->pin_groups[0]); + /* + * Set the devicetree configuration, but only if we just + * unregistered the device. Changing the configuration while + * the device is registered could cause a mismatch between the + * devicetree entry and the pins the device is actually using. + * This isn't a huge problem but the devicetree is our only + * source of pin information, so pin devices may silently fail + * to be registered if we mess up. + */ + if (bdev->init_unreg) { + if ((ret = set_device_config(bdev, NULL))) + goto err_init_prop; + } } + kfree(pathbuf); + return 0; + +err_init_prop: + for (bdev = platform_devices; bdev->name != NULL; bdev++) { + if (!bdev->node.path) + continue; + + if (bdev->node.of_node) + of_node_put(bdev->node.of_node); + if (bdev->aux_dev.of_node) + of_node_put(bdev->aux_dev.of_node); + } +err_init_chardev: + for (i = pin_min; i <= pin_max; i++) { + pin_dev = get_pin_device_by_pin(i); + if (pin_dev && pin_dev->device) + device_unregister(&pin_dev->device->dev); + } + kfree(pathbuf); +err_init_path: + kfree(pin_array); + pin_array = NULL; return ret; } +/* Register pin devices for all unused pins. */ +int pin_devices_init(void) +{ + struct pin_device *pdev; + struct device *dev; + struct bcm_device *bdev; + int ret; + u32 i; + + for (bdev = platform_devices; bdev->name != NULL; bdev++) { + if (!bdev->node.path) + continue; + + if (!(dev = find_device_by_node(bdev->node.of_node))) + continue; + + /* + * All pin devices have set_gpio set here. Clear the flag for + * all pins used by registered devices. + */ + device_for_each_pin(bdev->node.of_node, clear_gpio_flag); + put_device(dev); + } + + for (i = pin_min; i <= pin_max; i++) { + pdev = get_pin_device_by_pin(i); + if (pdev && pdev->set_gpio) { + if ((ret = register_pin_device(pdev))) + return ret; + } + } + + /* + * Prevent kernel messages from being written to the serial console. + * We can still view the messages with dmesg, but we want to stay silent + * when un/registering uart1 so connected serial devices don't see any + * "garbage" data. This is part of our compromise: ideally we wouldn't + * write anything to the serial pins until the user opens them with PIO, + * but we still need a way for developers to provision the device in + * case they don't have access to a wired network. + */ + console_silent(); + return 0; +} + /* - * Find the device using this pin. The returned device may be a pin device or - * peripheral device. We register pin devices for each pin when a peripheral - * device gets unregistered, so returning NULL here should never happen. The - * caller must call device_put on the returned device. + * Find the peripheral device using this pin, or return NULL if it isn't being + * used or is being used by a pin device. The caller must call put_device on the + * returned device. */ static struct device *find_active_device_by_pin(u32 pin, struct bcm_device **dev) @@ -418,23 +542,6 @@ static struct device *find_active_device_by_pin(u32 pin, return NULL; } -static int register_pin_device(struct pin_device *dev) -{ - return (!dev->device) ? register_device_by_node(dev->of_node) : 0; -} - -static int set_gpio_flag(struct pin_device *pin) -{ - pin->set_gpio = 1; - return 0; -} - -static int clear_gpio_flag(struct pin_device *pin) -{ - pin->set_gpio = 0; - return 0; -} - /* * Free a pin for use by a device. If this pin is set to GPIO simply unregister * its pin device. If this pin is owned by another device, unregister that @@ -606,7 +713,7 @@ static inline int replace_pin(struct bcm_device *bcm_dev, */ static inline int __set_function(struct pin_device *dev, struct bcm_device *bcm_dev, - struct pin_group *group) + struct pin_group *group) { struct device *new_dev; struct device *exdev; @@ -681,7 +788,7 @@ static inline int __set_function(struct pin_device *dev, device_for_each_pin(bcm_dev->node.of_node, clear_gpio_flag); /* Register a pin device for each pin we're not using. */ - for (i = 0, ret = 0; i < pin_count; i++) { + for (i = pin_min, ret = 0; i <= pin_max; i++) { if (!(pin_dev = get_pin_device_by_pin(i))) continue; @@ -703,7 +810,7 @@ static inline int __set_function(struct pin_device *dev, } int set_function(struct pin_device *dev, struct bcm_device *bcm_dev, - struct pin_group *group) + struct pin_group *group) { int ret; @@ -775,6 +882,7 @@ static inline int __set_resistor(struct pin_device *dev, u32 resistor) if (index == -1) { dev_err(pdev, "could not get resistor index for pin %d\n", dev->pin); + put_device(pdev); return -EINVAL; } } else { diff --git a/drivers/pinctrl/android-things/devicetree.c b/drivers/pinctrl/android-things/devicetree.c index 1d75e53bb1d8..f33f31662bf7 100644 --- a/drivers/pinctrl/android-things/devicetree.c +++ b/drivers/pinctrl/android-things/devicetree.c @@ -99,7 +99,6 @@ err_alloc_value: err_alloc_name: kfree(ret); err_alloc_struct: - pr_err(TAG "kmalloc failed %s:%d\n", __FILE__, __LINE__); return NULL; } diff --git a/drivers/pinctrl/android-things/main.c b/drivers/pinctrl/android-things/main.c index ad2813a84b70..e6bf2deaeae4 100644 --- a/drivers/pinctrl/android-things/main.c +++ b/drivers/pinctrl/android-things/main.c @@ -47,7 +47,7 @@ static int pin_probe(struct platform_device *dev) { struct pin_device *pin_dev; - if ((pin_dev = track_pin_device(dev, &pinctrl_class))) + if ((pin_dev = track_pin_device(dev))) dev_set_drvdata(&dev->dev, pin_dev); return 0; @@ -66,20 +66,25 @@ static int __init runtimepinconfig_init(void) goto err_class; } - if (unregister_platform_devices()) { - pr_err(TAG "unable to unregister platform devices\n"); - goto err_unregister_or_driver; + if (platform_devices_init(&pinctrl_class)) { + pr_err(TAG "unable to initialize platform devices\n"); + goto err_init_or_driver; } if (__platform_driver_register(&pin_driver, THIS_MODULE)) { pr_err(TAG "unable to register pin driver\n"); - goto err_unregister_or_driver; + goto err_init_or_driver; } - pr_debug(TAG "module loaded\n"); + if (pin_devices_init()) { + pr_err(TAG "unable to initialize pin devices\n"); + goto err_init_or_driver; + } + + pr_info(TAG "driver loaded\n"); return 0; -err_unregister_or_driver: +err_init_or_driver: class_unregister(&pinctrl_class); err_class: return -ECANCELED; @@ -100,4 +105,4 @@ static struct platform_driver pin_driver = { } }; -module_init(runtimepinconfig_init); +late_initcall(runtimepinconfig_init); diff --git a/drivers/pinctrl/android-things/platform_devices.c b/drivers/pinctrl/android-things/platform_devices.c index 97a6c0b59b9a..cfa04258709e 100644 --- a/drivers/pinctrl/android-things/platform_devices.c +++ b/drivers/pinctrl/android-things/platform_devices.c @@ -27,7 +27,11 @@ * devices depend on. */ -const int pin_count = 28; +const u32 pin_min = 2; +const u32 pin_max = 27; + +const char *pin_prefix = "BCM"; +const char *pin_path_prefix = "/soc/android-things-pins/BCM"; struct bcm_device platform_devices[] = { { @@ -36,6 +40,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = NULL }, .use_default = 0, .always_unreg_aux = 0, + .init_unreg = 0, .pin_count = 5, .pin_pull = (u32 []) { NONE, NONE, NONE, NONE, NONE }, .pin_group_count = 1, @@ -52,6 +57,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = "/soc/cprman@7e101000" }, .use_default = 0, .always_unreg_aux = 0, + .init_unreg = 1, .pin_count = 2, .pin_pull = (u32 []) { NONE, NONE, }, .pin_group_count = 2, @@ -74,6 +80,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = NULL }, .use_default = 0, .always_unreg_aux = 0, + .init_unreg = 1, .pin_count = 2, .pin_pull = (u32 []) { NONE, NONE }, .pin_group_count = 1, @@ -90,6 +97,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = NULL }, .use_default = 1, .always_unreg_aux = 0, + .init_unreg = 0, .pin_count = 2, .pin_pull = (u32 []) { NONE, UP }, .pin_group_count = 2, @@ -109,6 +117,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = NULL }, .use_default = 0, .always_unreg_aux = 0, + .init_unreg = 0, .pin_count = 2, .pin_pull = (u32 []) { NONE, UP }, .pin_group_count = 1, @@ -125,6 +134,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = "/soc/sound" }, .use_default = 0, .always_unreg_aux = 1, + .init_unreg = 1, .pin_count = 4, .pin_pull = (u32 []) { NONE, NONE, NONE, NONE }, .pin_group_count = 1, @@ -144,6 +154,7 @@ struct bcm_device platform_devices[] = { .aux_dev = { .path = NULL }, .use_default = 0, .always_unreg_aux = 0, + .init_unreg = 0, .pin_count = 26, .pin_group_count = 1, .pin_groups = (struct pin_group []) { diff --git a/drivers/pinctrl/android-things/platform_devices.h b/drivers/pinctrl/android-things/platform_devices.h index baea33c89f10..a8e6340de2f1 100644 --- a/drivers/pinctrl/android-things/platform_devices.h +++ b/drivers/pinctrl/android-things/platform_devices.h @@ -68,13 +68,16 @@ struct pin_group { * @use_default: Whether or not we should register this device on * a default pin_group if nobody is using it. For example, * uart0 on the Raspberry Pi 3 is also used for Bluetooth, - * so we need to register it again when the user stops + * so we need to register uart0 again when the user stops * using it directly. pin_groups[0] is the default * pin_group. * @always_unreg_aux: Whether or not we should unregister the auxiliary device * whenever we unregister this device. This flag also * controls the order in which the devices get * registered/unregistered. + * @init_unreg: Whether or not we should unregister the device upon loading the + * module. This is useful for mutually exclusive devices which all + * may be registered at boot. * @pin_count: The number of pins used by this peripheral. * @pin_pull: Specifies default resistor values for this device. Only used * with use_default. @@ -93,6 +96,7 @@ struct bcm_device { struct node_path aux_dev; int use_default:1; int always_unreg_aux:1; + int init_unreg:1; int pin_count; u32 *pin_pull; int pin_group_count; @@ -118,6 +122,9 @@ struct bcm_resistor { extern struct bcm_device platform_devices[]; extern struct bcm_resistor platform_resistors[]; -extern const int pin_count; +extern const u32 pin_min; +extern const u32 pin_max; +extern const char *pin_prefix; +extern const char *pin_path_prefix; #endif /* PLATFORM_DEVICES_H_ */ diff --git a/drivers/pinctrl/android-things/runtimepinconfig.h b/drivers/pinctrl/android-things/runtimepinconfig.h index b165436d65f9..3e61e87c9f86 100644 --- a/drivers/pinctrl/android-things/runtimepinconfig.h +++ b/drivers/pinctrl/android-things/runtimepinconfig.h @@ -59,11 +59,11 @@ int set_function(struct pin_device *dev, struct bcm_device *bcm_dev, struct pin_group *group); int set_resistor(struct pin_device *dev, u32 resistor); -struct pin_device *track_pin_device(struct platform_device *dev, - struct class *class); +struct pin_device *track_pin_device(struct platform_device *dev); void untrack_pin_device(struct pin_device *dev); -int unregister_platform_devices(void); +int platform_devices_init(struct class *class); +int pin_devices_init(void); int get_pin(struct device_node *node, u32 *pin); int device_has_pin(struct device_node *node, u32 pin); |