mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 18:35:42 +01:00 
			
		
		
		
	This xlate function just performs some checking. We can do this in request() instead and use the default xlate. Signed-off-by: Sean Anderson <seanga2@gmail.com> Reviewed-by: Tero Kristo <kristo@kernel.org> Link: https://lore.kernel.org/r/20211215164718.2778664-1-seanga2@gmail.com
		
			
				
	
	
		
			251 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Texas Instruments CDCE913/925/937/949 clock synthesizer driver
 | |
|  *
 | |
|  * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
 | |
|  *	Tero Kristo <t-kristo@ti.com>
 | |
|  *
 | |
|  * Based on Linux kernel clk-cdce925.c.
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <errno.h>
 | |
| #include <clk-uclass.h>
 | |
| #include <i2c.h>
 | |
| #include <dm/device_compat.h>
 | |
| #include <linux/bitops.h>
 | |
| 
 | |
| #define MAX_NUMBER_OF_PLLS		4
 | |
| #define MAX_NUMER_OF_OUTPUTS		9
 | |
| 
 | |
| #define CDCE9XX_REG_GLOBAL1		0x01
 | |
| #define CDCE9XX_REG_Y1SPIPDIVH		0x02
 | |
| #define CDCE9XX_REG_PDIV1L		0x03
 | |
| #define CDCE9XX_REG_XCSEL		0x05
 | |
| 
 | |
| #define CDCE9XX_PDIV1_H_MASK		0x3
 | |
| 
 | |
| #define CDCE9XX_REG_PDIV(clk)		(0x16 + (((clk) - 1) & 1) + \
 | |
| 					 ((clk) - 1) / 2 * 0x10)
 | |
| 
 | |
| #define CDCE9XX_PDIV_MASK		0x7f
 | |
| 
 | |
| #define CDCE9XX_BYTE_TRANSFER		BIT(7)
 | |
| 
 | |
| struct cdce9xx_chip_info {
 | |
| 	int num_plls;
 | |
| 	int num_outputs;
 | |
| };
 | |
| 
 | |
| struct cdce9xx_clk_data {
 | |
| 	struct udevice *i2c;
 | |
| 	struct cdce9xx_chip_info *chip;
 | |
| 	u32 xtal_rate;
 | |
| };
 | |
| 
 | |
| static const struct cdce9xx_chip_info cdce913_chip_info = {
 | |
| 	.num_plls = 1, .num_outputs = 3,
 | |
| };
 | |
| 
 | |
| static const struct cdce9xx_chip_info cdce925_chip_info = {
 | |
| 	.num_plls = 2, .num_outputs = 5,
 | |
| };
 | |
| 
 | |
| static const struct cdce9xx_chip_info cdce937_chip_info = {
 | |
| 	.num_plls = 3, .num_outputs = 7,
 | |
| };
 | |
| 
 | |
| static const struct cdce9xx_chip_info cdce949_chip_info = {
 | |
| 	.num_plls = 4, .num_outputs = 9,
 | |
| };
 | |
| 
 | |
| static int cdce9xx_reg_read(struct udevice *dev, u8 addr, u8 *buf)
 | |
| {
 | |
| 	struct cdce9xx_clk_data *data = dev_get_priv(dev);
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = dm_i2c_read(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, buf, 1);
 | |
| 	if (ret)
 | |
| 		dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
 | |
| 			addr, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int cdce9xx_reg_write(struct udevice *dev, u8 addr, u8 val)
 | |
| {
 | |
| 	struct cdce9xx_clk_data *data = dev_get_priv(dev);
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = dm_i2c_write(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, &val, 1);
 | |
| 	if (ret)
 | |
| 		dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
 | |
| 			addr, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int cdce9xx_clk_request(struct clk *clk)
 | |
| {
 | |
| 	struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
 | |
| 
 | |
| 	if (clk->id > data->chip->num_outputs)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int cdce9xx_clk_probe(struct udevice *dev)
 | |
| {
 | |
| 	struct cdce9xx_clk_data *data = dev_get_priv(dev);
 | |
| 	struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev);
 | |
| 	int ret;
 | |
| 	u32 val;
 | |
| 	struct clk clk;
 | |
| 
 | |
| 	val = (u32)dev_read_addr_ptr(dev);
 | |
| 
 | |
| 	ret = i2c_get_chip(dev->parent, val, 1, &data->i2c);
 | |
| 	if (ret) {
 | |
| 		dev_err(dev, "I2C probe failed.\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	data->chip = chip;
 | |
| 
 | |
| 	ret = clk_get_by_index(dev, 0, &clk);
 | |
| 	data->xtal_rate = clk_get_rate(&clk);
 | |
| 
 | |
| 	val = dev_read_u32_default(dev, "xtal-load-pf", -1);
 | |
| 	if (val >= 0)
 | |
| 		cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static u16 cdce9xx_clk_get_pdiv(struct clk *clk)
 | |
| {
 | |
| 	u8 val;
 | |
| 	u16 pdiv;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (clk->id == 0) {
 | |
| 		ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 | |
| 		if (ret)
 | |
| 			return 0;
 | |
| 
 | |
| 		pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8;
 | |
| 
 | |
| 		ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val);
 | |
| 		if (ret)
 | |
| 			return 0;
 | |
| 
 | |
| 		pdiv |= val;
 | |
| 	} else {
 | |
| 		ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 | |
| 				       &val);
 | |
| 		if (ret)
 | |
| 			return 0;
 | |
| 
 | |
| 		pdiv = val & CDCE9XX_PDIV_MASK;
 | |
| 	}
 | |
| 
 | |
| 	return pdiv;
 | |
| }
 | |
| 
 | |
| static u32 cdce9xx_clk_get_parent_rate(struct clk *clk)
 | |
| {
 | |
| 	struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
 | |
| 
 | |
| 	return data->xtal_rate;
 | |
| }
 | |
| 
 | |
| static ulong cdce9xx_clk_get_rate(struct clk *clk)
 | |
| {
 | |
| 	u32 parent_rate;
 | |
| 	u16 pdiv;
 | |
| 
 | |
| 	parent_rate = cdce9xx_clk_get_parent_rate(clk);
 | |
| 
 | |
| 	pdiv = cdce9xx_clk_get_pdiv(clk);
 | |
| 
 | |
| 	return parent_rate / pdiv;
 | |
| }
 | |
| 
 | |
| static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate)
 | |
| {
 | |
| 	u32 parent_rate;
 | |
| 	int pdiv;
 | |
| 	u32 diff;
 | |
| 	u8 val;
 | |
| 	int ret;
 | |
| 
 | |
| 	parent_rate = cdce9xx_clk_get_parent_rate(clk);
 | |
| 
 | |
| 	pdiv = parent_rate / rate;
 | |
| 
 | |
| 	diff = rate - parent_rate / pdiv;
 | |
| 
 | |
| 	if (rate - parent_rate / (pdiv + 1) < diff)
 | |
| 		pdiv++;
 | |
| 
 | |
| 	if (clk->id == 0) {
 | |
| 		ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		val &= ~CDCE9XX_PDIV1_H_MASK;
 | |
| 
 | |
| 		val |= (pdiv >> 8);
 | |
| 
 | |
| 		ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L,
 | |
| 					(pdiv & 0xff));
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	} else {
 | |
| 		ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 | |
| 				       &val);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		val &= ~CDCE9XX_PDIV_MASK;
 | |
| 
 | |
| 		val |= pdiv;
 | |
| 
 | |
| 		ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id),
 | |
| 					val);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct udevice_id cdce9xx_clk_of_match[] = {
 | |
| 	{ .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info },
 | |
| 	{ .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info },
 | |
| 	{ .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info },
 | |
| 	{ .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info },
 | |
| 	{ /* sentinel */ },
 | |
| };
 | |
| 
 | |
| static const struct clk_ops cdce9xx_clk_ops = {
 | |
| 	.request = cdce9xx_clk_request,
 | |
| 	.get_rate = cdce9xx_clk_get_rate,
 | |
| 	.set_rate = cdce9xx_clk_set_rate,
 | |
| };
 | |
| 
 | |
| U_BOOT_DRIVER(cdce9xx_clk) = {
 | |
| 	.name = "cdce9xx-clk",
 | |
| 	.id = UCLASS_CLK,
 | |
| 	.of_match = cdce9xx_clk_of_match,
 | |
| 	.probe = cdce9xx_clk_probe,
 | |
| 	.priv_auto	= sizeof(struct cdce9xx_clk_data),
 | |
| 	.ops = &cdce9xx_clk_ops,
 | |
| };
 |