clk: implement clk_set_defaults()
Linux uses the properties 'assigned-clocks', 'assigned-clock-parents' and 'assigned-clock-rates' to configure the clock subsystem for use with various peripheral nodes. This implements clk_set_defaults() and hooks it up with the general device probibin in drivers/core/device.c: when a new device is probed, clk_set_defaults() will be called for it and will process the properties mentioned above. Note that this functionality is designed to fail gracefully (i.e. if a clock-driver does not implement set_parent(), we simply accept this and ignore the error) as not to break existing board-support. Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com> Tested-by: David Wu <david.wu@rock-chips.com> Series-changes: 2 - Fixed David's email address. Series-version: 2 Cover-letter: clk: support assigned-clock, assigned-clock-parents, assigned-clock-rates For various peripherals on Rockchip SoCs (e.g. for the Ethernet GMAC), the parent-clock needs to be set via the DTS. This adds the required plumbing and implements the GMAC case for the RK3399. END
This commit is contained in:
		
							parent
							
								
									a45f17e8b9
								
							
						
					
					
						commit
						f4fcba5c5b
					
				|  | @ -2,6 +2,7 @@ | ||||||
|  * Copyright (C) 2015 Google, Inc |  * Copyright (C) 2015 Google, Inc | ||||||
|  * Written by Simon Glass <sjg@chromium.org> |  * Written by Simon Glass <sjg@chromium.org> | ||||||
|  * Copyright (c) 2016, NVIDIA CORPORATION. |  * Copyright (c) 2016, NVIDIA CORPORATION. | ||||||
|  |  * Copyright (c) 2018, Theobroma Systems Design und Consulting GmbH | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier:	GPL-2.0+ |  * SPDX-License-Identifier:	GPL-2.0+ | ||||||
|  */ |  */ | ||||||
|  | @ -10,6 +11,7 @@ | ||||||
| #include <clk.h> | #include <clk.h> | ||||||
| #include <clk-uclass.h> | #include <clk-uclass.h> | ||||||
| #include <dm.h> | #include <dm.h> | ||||||
|  | #include <dm/read.h> | ||||||
| #include <dt-structs.h> | #include <dt-structs.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| 
 | 
 | ||||||
|  | @ -101,6 +103,122 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) | ||||||
| { | { | ||||||
| 	return clk_get_by_indexed_prop(dev, "clocks", index, clk); | 	return clk_get_by_indexed_prop(dev, "clocks", index, clk); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static int clk_set_default_parents(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct clk clk, parent_clk; | ||||||
|  | 	int index; | ||||||
|  | 	int num_parents; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	num_parents = dev_count_phandle_with_args(dev, "assigned-clock-parents", | ||||||
|  | 						  "#clock-cells"); | ||||||
|  | 	if (num_parents < 0) { | ||||||
|  | 		debug("%s: could not read assigned-clock-parents for %p\n", | ||||||
|  | 		      __func__, dev); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (index = 0; index < num_parents; index++) { | ||||||
|  | 		ret = clk_get_by_indexed_prop(dev, "assigned-clock-parents", | ||||||
|  | 					      index, &parent_clk); | ||||||
|  | 		if (ret) { | ||||||
|  | 			debug("%s: could not get parent clock %d for %s\n", | ||||||
|  | 			      __func__, index, dev_read_name(dev)); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ret = clk_get_by_indexed_prop(dev, "assigned-clocks", | ||||||
|  | 					      index, &clk); | ||||||
|  | 		if (ret) { | ||||||
|  | 			debug("%s: could not get assigned clock %d for %s\n", | ||||||
|  | 			      __func__, index, dev_read_name(dev)); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ret = clk_set_parent(&clk, &parent_clk); | ||||||
|  | 
 | ||||||
|  | 		/*
 | ||||||
|  | 		 * Not all drivers may support clock-reparenting (as of now). | ||||||
|  | 		 * Ignore errors due to this. | ||||||
|  | 		 */ | ||||||
|  | 		if (ret == -ENOSYS) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (ret) { | ||||||
|  | 			debug("%s: failed to reparent clock %d for %s\n", | ||||||
|  | 			      __func__, index, dev_read_name(dev)); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int clk_set_default_rates(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct clk clk; | ||||||
|  | 	int index; | ||||||
|  | 	int num_rates; | ||||||
|  | 	int size; | ||||||
|  | 	int ret = 0; | ||||||
|  | 	u32 *rates = NULL; | ||||||
|  | 
 | ||||||
|  | 	size = dev_read_size(dev, "assigned-clock-rates"); | ||||||
|  | 	if (size < 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	num_rates = size / sizeof(u32); | ||||||
|  | 	rates = calloc(num_rates, sizeof(u32)); | ||||||
|  | 	if (!rates) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	ret = dev_read_u32_array(dev, "assigned-clock-rates", rates, num_rates); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	for (index = 0; index < num_rates; index++) { | ||||||
|  | 		ret = clk_get_by_indexed_prop(dev, "assigned-clocks", | ||||||
|  | 					      index, &clk); | ||||||
|  | 		if (ret) { | ||||||
|  | 			debug("%s: could not get assigned clock %d for %s\n", | ||||||
|  | 			      __func__, index, dev_read_name(dev)); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ret = clk_set_rate(&clk, rates[index]); | ||||||
|  | 		if (ret < 0) { | ||||||
|  | 			debug("%s: failed to set rate on clock %d for %s\n", | ||||||
|  | 			      __func__, index, dev_read_name(dev)); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | fail: | ||||||
|  | 	free(rates); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int clk_set_defaults(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	/* If this is running pre-reloc state, don't take any action. */ | ||||||
|  | 	if (!(gd->flags & GD_FLG_RELOC)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	debug("%s(%s)\n", __func__, dev_read_name(dev)); | ||||||
|  | 
 | ||||||
|  | 	ret = clk_set_default_parents(dev); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	ret = clk_set_default_rates(dev); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
| # endif /* OF_PLATDATA */ | # endif /* OF_PLATDATA */ | ||||||
| 
 | 
 | ||||||
| int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) | int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <common.h> | #include <common.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
|  | #include <clk.h> | ||||||
| #include <fdtdec.h> | #include <fdtdec.h> | ||||||
| #include <fdt_support.h> | #include <fdt_support.h> | ||||||
| #include <malloc.h> | #include <malloc.h> | ||||||
|  | @ -391,6 +392,11 @@ int device_probe(struct udevice *dev) | ||||||
| 			goto fail; | 			goto fail; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ | ||||||
|  | 	ret = clk_set_defaults(dev); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
| 	if (drv->probe) { | 	if (drv->probe) { | ||||||
| 		ret = drv->probe(dev); | 		ret = drv->probe(dev); | ||||||
| 		if (ret) { | 		if (ret) { | ||||||
|  |  | ||||||
|  | @ -133,6 +133,23 @@ static inline int clk_release_all(struct clk *clk, int count) | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) && \ | ||||||
|  | 	CONFIG_IS_ENABLED(CLK) | ||||||
|  | /**
 | ||||||
|  |  * clk_set_defaults - Process 'assigned-{clocks/clock-parents/clock-rates}' | ||||||
|  |  *                    properties to configure clocks | ||||||
|  |  * | ||||||
|  |  * @dev:        A device to process (the ofnode associated with this device | ||||||
|  |  *              will be processed). | ||||||
|  |  */ | ||||||
|  | int clk_set_defaults(struct udevice *dev); | ||||||
|  | #else | ||||||
|  | static inline int clk_set_defaults(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * clk_request - Request a clock by provider-specific ID. |  * clk_request - Request a clock by provider-specific ID. | ||||||
|  * |  * | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue