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/video/Kconfig" | ||||||
| 
 | 
 | ||||||
|  | source "drivers/w1/Kconfig" | ||||||
|  | 
 | ||||||
| source "drivers/watchdog/Kconfig" | source "drivers/watchdog/Kconfig" | ||||||
| 
 | 
 | ||||||
| config PHYS_TO_BUS | config PHYS_TO_BUS | ||||||
|  |  | ||||||
|  | @ -105,6 +105,7 @@ obj-y += smem/ | ||||||
| obj-y += soc/ | obj-y += soc/ | ||||||
| obj-y += thermal/ | obj-y += thermal/ | ||||||
| obj-y += axi/ | obj-y += axi/ | ||||||
|  | obj-$(CONFIG_W1) += w1/ | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ | obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ | ||||||
| endif | 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,		/* Video or LCD device */ | ||||||
| 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */ | 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */ | ||||||
| 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */ | 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */ | ||||||
|  | 	UCLASS_W1,		/* Dallas 1-Wire bus */ | ||||||
| 	UCLASS_WDT,		/* Watchdot Timer driver */ | 	UCLASS_WDT,		/* Watchdot Timer driver */ | ||||||
| 
 | 
 | ||||||
| 	UCLASS_COUNT, | 	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