w1: Add 1-Wire uclass
We might want to use 1-Wire devices connected on boards such as EEPROMs in U-Boot. Provide a framework to be able to do that. Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> [eugen.hristev@microchip.com: reworked] Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
This commit is contained in:
		
							parent
							
								
									620300043c
								
							
						
					
					
						commit
						d3e19cf919
					
				|  | @ -106,6 +106,8 @@ source "drivers/usb/Kconfig" | |||
| 
 | ||||
| source "drivers/video/Kconfig" | ||||
| 
 | ||||
| source "drivers/w1/Kconfig" | ||||
| 
 | ||||
| source "drivers/watchdog/Kconfig" | ||||
| 
 | ||||
| config PHYS_TO_BUS | ||||
|  |  | |||
|  | @ -105,6 +105,7 @@ obj-y += smem/ | |||
| obj-y += soc/ | ||||
| obj-y += thermal/ | ||||
| obj-y += axi/ | ||||
| obj-$(CONFIG_W1) += w1/ | ||||
| 
 | ||||
| obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ | ||||
| endif | ||||
|  |  | |||
|  | @ -0,0 +1,18 @@ | |||
| # | ||||
| # W1 subsystem configuration | ||||
| # | ||||
| 
 | ||||
| menu "1-Wire support" | ||||
| 
 | ||||
| config W1 | ||||
| 	bool "Enable 1-wire controllers support" | ||||
| 	default no | ||||
| 	depends on DM | ||||
| 	help | ||||
| 	  Support for the Dallas 1-Wire bus. | ||||
| 
 | ||||
| if W1 | ||||
| 
 | ||||
| endif | ||||
| 
 | ||||
| endmenu | ||||
|  | @ -0,0 +1 @@ | |||
| obj-$(CONFIG_W1) += w1-uclass.o | ||||
|  | @ -0,0 +1,236 @@ | |||
| // SPDX-License-Identifier:	GPL-2.0+
 | ||||
| /*
 | ||||
|  * | ||||
|  * Copyright (c) 2015 Free Electrons | ||||
|  * Copyright (c) 2015 NextThing Co. | ||||
|  * Copyright (c) 2018 Microchip Technology, Inc. | ||||
|  * | ||||
|  * Maxime Ripard <maxime.ripard@free-electrons.com> | ||||
|  * Eugen Hristev <eugen.hristev@microchip.com> | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <common.h> | ||||
| #include <dm.h> | ||||
| #include <w1.h> | ||||
| 
 | ||||
| #include <dm/device-internal.h> | ||||
| 
 | ||||
| #define W1_MATCH_ROM	0x55 | ||||
| #define W1_SKIP_ROM	0xcc | ||||
| #define W1_SEARCH	0xf0 | ||||
| 
 | ||||
| struct w1_bus { | ||||
| 	u64	search_id; | ||||
| }; | ||||
| 
 | ||||
| static int w1_enumerate(struct udevice *bus) | ||||
| { | ||||
| 	const struct w1_ops *ops = device_get_ops(bus); | ||||
| 	struct w1_bus *w1 = dev_get_uclass_priv(bus); | ||||
| 	u64 last_rn, rn = w1->search_id, tmp64; | ||||
| 	bool last_device = false; | ||||
| 	int search_bit, desc_bit = 64; | ||||
| 	int last_zero = -1; | ||||
| 	u8 triplet_ret = 0; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!ops->reset || !ops->write_byte || !ops->triplet) | ||||
| 		return -ENOSYS; | ||||
| 
 | ||||
| 	while (!last_device) { | ||||
| 		last_rn = rn; | ||||
| 		rn = 0; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Reset bus and all 1-wire device state machines | ||||
| 		 * so they can respond to our requests. | ||||
| 		 * | ||||
| 		 * Return 0 - device(s) present, 1 - no devices present. | ||||
| 		 */ | ||||
| 		if (ops->reset(bus)) { | ||||
| 			debug("%s: No devices present on the wire.\n", | ||||
| 			      __func__); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Start the search */ | ||||
| 		ops->write_byte(bus, W1_SEARCH); | ||||
| 		for (i = 0; i < 64; ++i) { | ||||
| 			/* Determine the direction/search bit */ | ||||
| 			if (i == desc_bit) | ||||
| 				/* took the 0 path last time, so take the 1 path */ | ||||
| 				search_bit = 1; | ||||
| 			else if (i > desc_bit) | ||||
| 				/* take the 0 path on the next branch */ | ||||
| 				search_bit = 0; | ||||
| 			else | ||||
| 				search_bit = ((last_rn >> i) & 0x1); | ||||
| 
 | ||||
| 			/* Read two bits and write one bit */ | ||||
| 			triplet_ret = ops->triplet(bus, search_bit); | ||||
| 
 | ||||
| 			/* quit if no device responded */ | ||||
| 			if ((triplet_ret & 0x03) == 0x03) | ||||
| 				break; | ||||
| 
 | ||||
| 			/* If both directions were valid, and we took the 0 path... */ | ||||
| 			if (triplet_ret == 0) | ||||
| 				last_zero = i; | ||||
| 
 | ||||
| 			/* extract the direction taken & update the device number */ | ||||
| 			tmp64 = (triplet_ret >> 2); | ||||
| 			rn |= (tmp64 << i); | ||||
| 		} | ||||
| 
 | ||||
| 		/* last device or error, aborting here */ | ||||
| 		if ((triplet_ret & 0x03) == 0x03) | ||||
| 			last_device = true; | ||||
| 
 | ||||
| 		if ((triplet_ret & 0x03) != 0x03) { | ||||
| 			if (desc_bit == last_zero || last_zero < 0) { | ||||
| 				last_device = 1; | ||||
| 				w1->search_id = 0; | ||||
| 			} else { | ||||
| 				w1->search_id = rn; | ||||
| 			} | ||||
| 			desc_bit = last_zero; | ||||
| 
 | ||||
| 			debug("%s: Detected new device 0x%llx (family 0x%x)\n", | ||||
| 			      bus->name, rn, (u8)(rn & 0xff)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int w1_get_bus(int busnum, struct udevice **busp) | ||||
| { | ||||
| 	int ret, i = 0; | ||||
| 
 | ||||
| 	struct udevice *dev; | ||||
| 
 | ||||
| 	for (ret = uclass_first_device(UCLASS_W1, &dev); | ||||
| 	     !ret; | ||||
| 	     uclass_next_device(&dev), i++) { | ||||
| 		if (ret) { | ||||
| 			debug("Cannot find w1 bus %d\n", busnum); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		if (i == busnum) { | ||||
| 			*busp = dev; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| u8 w1_get_device_family(struct udevice *dev) | ||||
| { | ||||
| 	struct w1_device *w1 = dev_get_parent_platdata(dev); | ||||
| 
 | ||||
| 	return w1->id & 0xff; | ||||
| } | ||||
| 
 | ||||
| int w1_reset_select(struct udevice *dev) | ||||
| { | ||||
| 	struct w1_device *w1 = dev_get_parent_platdata(dev); | ||||
| 	struct udevice *bus = dev_get_parent(dev); | ||||
| 	const struct w1_ops *ops = device_get_ops(bus); | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!ops->reset || !ops->write_byte) | ||||
| 		return -ENOSYS; | ||||
| 
 | ||||
| 	ops->reset(bus); | ||||
| 
 | ||||
| 	ops->write_byte(bus, W1_MATCH_ROM); | ||||
| 
 | ||||
| 	for (i = 0; i < sizeof(w1->id); i++) | ||||
| 		ops->write_byte(bus, (w1->id >> (i * 8)) & 0xff); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int w1_read_byte(struct udevice *dev) | ||||
| { | ||||
| 	struct udevice *bus = dev_get_parent(dev); | ||||
| 	const struct w1_ops *ops = device_get_ops(bus); | ||||
| 
 | ||||
| 	if (!ops->read_byte) | ||||
| 		return -ENOSYS; | ||||
| 
 | ||||
| 	return ops->read_byte(bus); | ||||
| } | ||||
| 
 | ||||
| int w1_read_buf(struct udevice *dev, u8 *buf, unsigned int count) | ||||
| { | ||||
| 	int i, ret; | ||||
| 
 | ||||
| 	for (i = 0; i < count; i++) { | ||||
| 		ret = w1_read_byte(dev); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		buf[i] = ret & 0xff; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int w1_write_byte(struct udevice *dev, u8 byte) | ||||
| { | ||||
| 	struct udevice *bus = dev_get_parent(dev); | ||||
| 	const struct w1_ops *ops = device_get_ops(bus); | ||||
| 
 | ||||
| 	if (!ops->write_byte) | ||||
| 		return -ENOSYS; | ||||
| 
 | ||||
| 	ops->write_byte(bus, byte); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int w1_post_probe(struct udevice *bus) | ||||
| { | ||||
| 	w1_enumerate(bus); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int w1_init(void) | ||||
| { | ||||
| 	struct udevice *bus; | ||||
| 	struct uclass *uc; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = uclass_get(UCLASS_W1, &uc); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	uclass_foreach_dev(bus, uc) { | ||||
| 		ret = device_probe(bus); | ||||
| 		if (ret == -ENODEV) {	/* No such device. */ | ||||
| 			printf("W1 controller not available.\n"); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (ret) {		/* Other error. */ | ||||
| 			printf("W1 controller probe failed.\n"); | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| UCLASS_DRIVER(w1) = { | ||||
| 	.name		= "w1", | ||||
| 	.id		= UCLASS_W1, | ||||
| 	.flags		= DM_UC_FLAG_SEQ_ALIAS, | ||||
| 	.per_device_auto_alloc_size	= sizeof(struct w1_bus), | ||||
| 	.post_probe	= w1_post_probe, | ||||
| #if CONFIG_IS_ENABLED(OF_CONTROL) | ||||
| 	.post_bind	= dm_scan_fdt_dev, | ||||
| #endif | ||||
| 	.per_child_platdata_auto_alloc_size     = sizeof(struct w1_device), | ||||
| }; | ||||
|  | @ -93,6 +93,7 @@ enum uclass_id { | |||
| 	UCLASS_VIDEO,		/* Video or LCD device */ | ||||
| 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */ | ||||
| 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */ | ||||
| 	UCLASS_W1,		/* Dallas 1-Wire bus */ | ||||
| 	UCLASS_WDT,		/* Watchdot Timer driver */ | ||||
| 
 | ||||
| 	UCLASS_COUNT, | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| /* SPDX-License-Identifier:	GPL-2.0+
 | ||||
|  * | ||||
|  * Copyright (c) 2015 Free Electrons | ||||
|  * Copyright (c) 2015 NextThing Co | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __W1_H | ||||
| #define __W1_H | ||||
| 
 | ||||
| #include <dm.h> | ||||
| 
 | ||||
| #define W1_FAMILY_DS24B33	0x23 | ||||
| #define W1_FAMILY_DS2431	0x2d | ||||
| 
 | ||||
| struct w1_device { | ||||
| 	u64	id; | ||||
| }; | ||||
| 
 | ||||
| struct w1_ops { | ||||
| 	u8	(*read_byte)(struct udevice *dev); | ||||
| 	bool	(*reset)(struct udevice *dev); | ||||
| 	u8	(*triplet)(struct udevice *dev, bool bdir); | ||||
| 	void	(*write_byte)(struct udevice *dev, u8 byte); | ||||
| }; | ||||
| 
 | ||||
| int w1_get_bus(int busnum, struct udevice **busp); | ||||
| u8 w1_get_device_family(struct udevice *dev); | ||||
| 
 | ||||
| int w1_read_buf(struct udevice *dev, u8 *buf, unsigned int count); | ||||
| int w1_read_byte(struct udevice *dev); | ||||
| int w1_reset_select(struct udevice *dev); | ||||
| int w1_write_buf(struct udevice *dev, u8 *buf, unsigned int count); | ||||
| int w1_write_byte(struct udevice *dev, u8 byte); | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
		Reference in New Issue