diff options
Diffstat (limited to 'drivers/w1')
35 files changed, 3942 insertions, 1164 deletions
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index fd2c7bd9dfb..6743bde038c 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -16,7 +16,7 @@ config W1_CON  	depends on CONNECTOR  	bool "Userspace communication over connector"  	default y -	--- help --- +	---help---  	  This allows to communicate with userspace using connector. For more  	  information see <file:Documentation/connector/connector.txt>.  	  There are three types of messages between w1 core and userspace: diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 80b3b123dd7..1708b2300c7 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -26,7 +26,7 @@ config W1_MASTER_DS2490  config W1_MASTER_DS2482  	tristate "Maxim DS2482 I2C to 1-Wire bridge" -	depends on I2C && EXPERIMENTAL +	depends on I2C  	help  	  If you say yes here you get support for the Maxim DS2482  	  I2C to 1-Wire bridge. @@ -36,13 +36,12 @@ config W1_MASTER_DS2482  config W1_MASTER_MXC  	tristate "Freescale MXC 1-wire busmaster" -	depends on W1 && ARCH_MXC +	depends on ARCH_MXC || COMPILE_TEST  	help  	  Say Y here to enable MXC 1-wire host  config W1_MASTER_DS1WM  	tristate "Maxim DS1WM 1-wire busmaster" -	depends on W1 && ARM && HAVE_CLK  	help  	  Say Y here to enable the DS1WM 1-wire driver, such as that  	  in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like @@ -50,7 +49,7 @@ config W1_MASTER_DS1WM  config W1_MASTER_GPIO  	tristate "GPIO 1-wire busmaster" -	depends on GENERIC_GPIO +	depends on GPIOLIB  	help  	  Say Y here if you want to communicate with your 1-wire devices using  	  GPIO pins. This driver uses the GPIO API to control the wire. @@ -60,7 +59,7 @@ config W1_MASTER_GPIO  config HDQ_MASTER_OMAP  	tristate "OMAP HDQ driver" -	depends on ARCH_OMAP2430 || ARCH_OMAP3 +	depends on ARCH_OMAP  	help  	  Say Y here if you want support for the 1-wire or HDQ Interface  	  on an OMAP processor. diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 6b85e7fefa4..02df3b1381d 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -13,6 +13,7 @@  #include <linux/module.h>  #include <linux/interrupt.h> +#include <linux/io.h>  #include <linux/irq.h>  #include <linux/pm.h>  #include <linux/platform_device.h> @@ -33,6 +34,7 @@  #define DS1WM_INT	0x02	/* R/W interrupt status */  #define DS1WM_INT_EN	0x03	/* R/W interrupt enable */  #define DS1WM_CLKDIV	0x04	/* R/W 5 bits of divisor and pre-scale */ +#define DS1WM_CNTRL	0x05	/* R/W master control register (not used yet) */  #define DS1WM_CMD_1W_RESET  (1 << 0)	/* force reset on 1-wire bus */  #define DS1WM_CMD_SRA	    (1 << 1)	/* enable Search ROM accelerator mode */ @@ -56,6 +58,7 @@  #define DS1WM_INTEN_ERSRF   (1 << 5)	/* enable rx shift register full int */  #define DS1WM_INTEN_DQO	    (1 << 6)	/* enable direct bus driving ops */ +#define DS1WM_INTEN_NOT_IAS (~DS1WM_INTEN_IAS)	/* all but INTR active state */  #define DS1WM_TIMEOUT (HZ * 5) @@ -63,41 +66,51 @@ static struct {  	unsigned long freq;  	unsigned long divisor;  } freq[] = { -	{ 4000000, 0x8 }, -	{ 5000000, 0x2 }, -	{ 6000000, 0x5 }, -	{ 7000000, 0x3 }, -	{ 8000000, 0xc }, -	{ 10000000, 0x6 }, -	{ 12000000, 0x9 }, -	{ 14000000, 0x7 }, -	{ 16000000, 0x10 }, -	{ 20000000, 0xa }, -	{ 24000000, 0xd }, -	{ 28000000, 0xb }, -	{ 32000000, 0x14 }, -	{ 40000000, 0xe }, -	{ 48000000, 0x11 }, -	{ 56000000, 0xf }, -	{ 64000000, 0x18 }, -	{ 80000000, 0x12 }, -	{ 96000000, 0x15 }, -	{ 112000000, 0x13 }, -	{ 128000000, 0x1c }, +	{   1000000, 0x80 }, +	{   2000000, 0x84 }, +	{   3000000, 0x81 }, +	{   4000000, 0x88 }, +	{   5000000, 0x82 }, +	{   6000000, 0x85 }, +	{   7000000, 0x83 }, +	{   8000000, 0x8c }, +	{  10000000, 0x86 }, +	{  12000000, 0x89 }, +	{  14000000, 0x87 }, +	{  16000000, 0x90 }, +	{  20000000, 0x8a }, +	{  24000000, 0x8d }, +	{  28000000, 0x8b }, +	{  32000000, 0x94 }, +	{  40000000, 0x8e }, +	{  48000000, 0x91 }, +	{  56000000, 0x8f }, +	{  64000000, 0x98 }, +	{  80000000, 0x92 }, +	{  96000000, 0x95 }, +	{ 112000000, 0x93 }, +	{ 128000000, 0x9c }, +/* you can continue this table, consult the OPERATION - CLOCK DIVISOR +   section of the ds1wm spec sheet. */  };  struct ds1wm_data { -	void		__iomem *map; -	int		bus_shift; /* # of shifts to calc register offsets */ +	void     __iomem *map; +	int      bus_shift; /* # of shifts to calc register offsets */  	struct platform_device *pdev; -	struct mfd_cell	*cell; -	int		irq; -	int		active_high; -	int		slave_present; -	void		*reset_complete; -	void		*read_complete; -	void		*write_complete; -	u8		read_byte; /* last byte received */ +	const struct mfd_cell   *cell; +	int      irq; +	int      slave_present; +	void     *reset_complete; +	void     *read_complete; +	void     *write_complete; +	int      read_error; +	/* last byte received */ +	u8       read_byte; +	/* byte to write that makes all intr disabled, */ +	/* considering active_state (IAS) (optimization) */ +	u8       int_en_reg_none; +	unsigned int reset_recover_delay; /* see ds1wm.h */  };  static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg, @@ -115,23 +128,39 @@ static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg)  static irqreturn_t ds1wm_isr(int isr, void *data)  {  	struct ds1wm_data *ds1wm_data = data; -	u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT); +	u8 intr; +	u8 inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN); +	/* if no bits are set in int enable register (except the IAS) +	than go no further, reading the regs below has side effects */ +	if (!(inten & DS1WM_INTEN_NOT_IAS)) +		return IRQ_NONE; -	ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1; +	ds1wm_write_register(ds1wm_data, +		DS1WM_INT_EN, ds1wm_data->int_en_reg_none); -	if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) -		complete(ds1wm_data->reset_complete); +	/* this read action clears the INTR and certain flags in ds1wm */ +	intr = ds1wm_read_register(ds1wm_data, DS1WM_INT); -	if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) -		complete(ds1wm_data->write_complete); +	ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1; +	if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) { +		inten &= ~DS1WM_INTEN_ETMT; +		complete(ds1wm_data->write_complete); +	}  	if (intr & DS1WM_INT_RBF) { +		/* this read clears the RBF flag */  		ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data, -							    DS1WM_DATA); +		DS1WM_DATA); +		inten &= ~DS1WM_INTEN_ERBF;  		if (ds1wm_data->read_complete)  			complete(ds1wm_data->read_complete);  	} +	if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) { +		inten &= ~DS1WM_INTEN_EPD; +		complete(ds1wm_data->reset_complete); +	} +	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, inten);  	return IRQ_HANDLED;  } @@ -142,63 +171,73 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data)  	ds1wm_data->reset_complete = &reset_done; +	/* enable Presence detect only */  	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD | -		(ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0)); +	ds1wm_data->int_en_reg_none);  	ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET);  	timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT);  	ds1wm_data->reset_complete = NULL;  	if (!timeleft) { -		dev_err(&ds1wm_data->pdev->dev, "reset failed\n"); +		dev_err(&ds1wm_data->pdev->dev, "reset failed, timed out\n");  		return 1;  	} -	/* Wait for the end of the reset. According to the specs, the time -	 * from when the interrupt is asserted to the end of the reset is: -	 *     tRSTH  - tPDH  - tPDL - tPDI -	 *     625 us - 60 us - 240 us - 100 ns = 324.9 us -	 * -	 * We'll wait a bit longer just to be sure. -	 * Was udelay(500), but if it is going to busywait the cpu that long, -	 * might as well come back later. -	 */ -	msleep(1); - -	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, -		DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD | -		(ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0)); -  	if (!ds1wm_data->slave_present) {  		dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n");  		return 1;  	} +	if (ds1wm_data->reset_recover_delay) +		msleep(ds1wm_data->reset_recover_delay); +  	return 0;  }  static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data)  { +	unsigned long timeleft;  	DECLARE_COMPLETION_ONSTACK(write_done);  	ds1wm_data->write_complete = &write_done; +	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, +	ds1wm_data->int_en_reg_none | DS1WM_INTEN_ETMT); +  	ds1wm_write_register(ds1wm_data, DS1WM_DATA, data); -	wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT); +	timeleft = wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT); +  	ds1wm_data->write_complete = NULL; +	if (!timeleft) { +		dev_err(&ds1wm_data->pdev->dev, "write failed, timed out\n"); +		return -ETIMEDOUT; +	}  	return 0;  } -static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data) +static u8 ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)  { +	unsigned long timeleft; +	u8 intEnable = DS1WM_INTEN_ERBF | ds1wm_data->int_en_reg_none;  	DECLARE_COMPLETION_ONSTACK(read_done); + +	ds1wm_read_register(ds1wm_data, DS1WM_DATA); +  	ds1wm_data->read_complete = &read_done; +	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, intEnable); -	ds1wm_write(ds1wm_data, write_data); -	wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT); -	ds1wm_data->read_complete = NULL; +	ds1wm_write_register(ds1wm_data, DS1WM_DATA, write_data); +	timeleft = wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT); +	ds1wm_data->read_complete = NULL; +	if (!timeleft) { +		dev_err(&ds1wm_data->pdev->dev, "read failed, timed out\n"); +		ds1wm_data->read_error = -ETIMEDOUT; +		return 0xFF; +	} +	ds1wm_data->read_error = 0;  	return ds1wm_data->read_byte;  } @@ -206,8 +245,8 @@ static int ds1wm_find_divisor(int gclk)  {  	int i; -	for (i = 0; i < ARRAY_SIZE(freq); i++) -		if (gclk <= freq[i].freq) +	for (i = ARRAY_SIZE(freq)-1; i >= 0; --i) +		if (gclk >= freq[i].freq)  			return freq[i].divisor;  	return 0; @@ -216,15 +255,17 @@ static int ds1wm_find_divisor(int gclk)  static void ds1wm_up(struct ds1wm_data *ds1wm_data)  {  	int divisor; -	struct ds1wm_driver_data *plat = ds1wm_data->cell->driver_data; +	struct device *dev = &ds1wm_data->pdev->dev; +	struct ds1wm_driver_data *plat = dev_get_platdata(dev);  	if (ds1wm_data->cell->enable)  		ds1wm_data->cell->enable(ds1wm_data->pdev);  	divisor = ds1wm_find_divisor(plat->clock_rate); +	dev_dbg(dev, "found divisor 0x%x for clock %d\n", +		divisor, plat->clock_rate);  	if (divisor == 0) { -		dev_err(&ds1wm_data->pdev->dev, -			"no suitable divisor for %dHz clock\n", +		dev_err(dev, "no suitable divisor for %dHz clock\n",  			plat->clock_rate);  		return;  	} @@ -242,7 +283,7 @@ static void ds1wm_down(struct ds1wm_data *ds1wm_data)  	/* Disable interrupts. */  	ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, -			     ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0); +		ds1wm_data->int_en_reg_none);  	if (ds1wm_data->cell->disable)  		ds1wm_data->cell->disable(ds1wm_data->pdev); @@ -279,41 +320,125 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,  {  	struct ds1wm_data *ds1wm_data = data;  	int i; -	unsigned long long rom_id; - -	/* XXX We need to iterate for multiple devices per the DS1WM docs. -	 * See http://www.maxim-ic.com/appnotes.cfm/appnote_number/120. */ -	if (ds1wm_reset(ds1wm_data)) -		return; - -	ds1wm_write(ds1wm_data, search_type); -	ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); - -	for (rom_id = 0, i = 0; i < 16; i++) { - -		unsigned char resp, r, d; - -		resp = ds1wm_read(ds1wm_data, 0x00); - -		r = ((resp & 0x02) >> 1) | -		    ((resp & 0x08) >> 2) | -		    ((resp & 0x20) >> 3) | -		    ((resp & 0x80) >> 4); - -		d = ((resp & 0x01) >> 0) | -		    ((resp & 0x04) >> 1) | -		    ((resp & 0x10) >> 2) | -		    ((resp & 0x40) >> 3); - -		rom_id |= (unsigned long long) r << (i * 4); - -	} -	dev_dbg(&ds1wm_data->pdev->dev, "found 0x%08llX\n", rom_id); - -	ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); -	ds1wm_reset(ds1wm_data); - -	slave_found(master_dev, rom_id); +	int ms_discrep_bit = -1; +	u64 r = 0; /* holds the progress of the search */ +	u64 r_prime, d; +	unsigned slaves_found = 0; +	unsigned int pass = 0; + +	dev_dbg(&ds1wm_data->pdev->dev, "search begin\n"); +	while (true) { +		++pass; +		if (pass > 100) { +			dev_dbg(&ds1wm_data->pdev->dev, +				"too many attempts (100), search aborted\n"); +			return; +		} + +		mutex_lock(&master_dev->bus_mutex); +		if (ds1wm_reset(ds1wm_data)) { +			mutex_unlock(&master_dev->bus_mutex); +			dev_dbg(&ds1wm_data->pdev->dev, +				"pass: %d reset error (or no slaves)\n", pass); +			break; +		} + +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d r : %0#18llx writing SEARCH_ROM\n", pass, r); +		ds1wm_write(ds1wm_data, search_type); +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d entering ASM\n", pass); +		ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d beginning nibble loop\n", pass); + +		r_prime = 0; +		d = 0; +		/* we work one nibble at a time */ +		/* each nibble is interleaved to form a byte */ +		for (i = 0; i < 16; i++) { + +			unsigned char resp, _r, _r_prime, _d; + +			_r = (r >> (4*i)) & 0xf; +			_r = ((_r & 0x1) << 1) | +			((_r & 0x2) << 2) | +			((_r & 0x4) << 3) | +			((_r & 0x8) << 4); + +			/* writes _r, then reads back: */ +			resp = ds1wm_read(ds1wm_data, _r); + +			if (ds1wm_data->read_error) { +				dev_err(&ds1wm_data->pdev->dev, +				"pass: %d nibble: %d read error\n", pass, i); +				break; +			} + +			_r_prime = ((resp & 0x02) >> 1) | +			((resp & 0x08) >> 2) | +			((resp & 0x20) >> 3) | +			((resp & 0x80) >> 4); + +			_d = ((resp & 0x01) >> 0) | +			((resp & 0x04) >> 1) | +			((resp & 0x10) >> 2) | +			((resp & 0x40) >> 3); + +			r_prime |= (unsigned long long) _r_prime << (i * 4); +			d |= (unsigned long long) _d << (i * 4); + +		} +		if (ds1wm_data->read_error) { +			mutex_unlock(&master_dev->bus_mutex); +			dev_err(&ds1wm_data->pdev->dev, +				"pass: %d read error, retrying\n", pass); +			break; +		} +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d r\': %0#18llx d:%0#18llx\n", +			pass, r_prime, d); +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d nibble loop complete, exiting ASM\n", pass); +		ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d resetting bus\n", pass); +		ds1wm_reset(ds1wm_data); +		mutex_unlock(&master_dev->bus_mutex); +		if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) { +			dev_err(&ds1wm_data->pdev->dev, +				"pass: %d bus error, retrying\n", pass); +			continue; /* start over */ +		} + + +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d found %0#18llx\n", pass, r_prime); +		slave_found(master_dev, r_prime); +		++slaves_found; +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d complete, preparing next pass\n", pass); + +		/* any discrepency found which we already choose the +		   '1' branch is now is now irrelevant we reveal the +		   next branch with this: */ +		d &= ~r; +		/* find last bit set, i.e. the most signif. bit set */ +		ms_discrep_bit = fls64(d) - 1; +		dev_dbg(&ds1wm_data->pdev->dev, +			"pass: %d new d:%0#18llx MS discrep bit:%d\n", +			pass, d, ms_discrep_bit); + +		/* prev_ms_discrep_bit = ms_discrep_bit; +		   prepare for next ROM search:		    */ +		if (ms_discrep_bit == -1) +			break; + +		r = (r &  ~(~0ull << (ms_discrep_bit))) | 1 << ms_discrep_bit; +	} /* end while true */ +	dev_dbg(&ds1wm_data->pdev->dev, +		"pass: %d total: %d search done ms d bit pos: %d\n", pass, +		slaves_found, ms_discrep_bit);  }  /* --------------------------------------------------------------------- */ @@ -330,57 +455,52 @@ static int ds1wm_probe(struct platform_device *pdev)  	struct ds1wm_data *ds1wm_data;  	struct ds1wm_driver_data *plat;  	struct resource *res; -	struct mfd_cell *cell;  	int ret;  	if (!pdev)  		return -ENODEV; -	cell = pdev->dev.platform_data; -	if (!cell) -		return -ENODEV; - -	ds1wm_data = kzalloc(sizeof(*ds1wm_data), GFP_KERNEL); +	ds1wm_data = devm_kzalloc(&pdev->dev, sizeof(*ds1wm_data), GFP_KERNEL);  	if (!ds1wm_data)  		return -ENOMEM;  	platform_set_drvdata(pdev, ds1wm_data);  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		ret = -ENXIO; -		goto err0; -	} -	ds1wm_data->map = ioremap(res->start, resource_size(res)); -	if (!ds1wm_data->map) { -		ret = -ENOMEM; -		goto err0; -	} -	plat = cell->driver_data; +	if (!res) +		return -ENXIO; +	ds1wm_data->map = devm_ioremap(&pdev->dev, res->start, +				       resource_size(res)); +	if (!ds1wm_data->map) +		return -ENOMEM;  	/* calculate bus shift from mem resource */  	ds1wm_data->bus_shift = resource_size(res) >> 3;  	ds1wm_data->pdev = pdev; -	ds1wm_data->cell = cell; +	ds1wm_data->cell = mfd_get_cell(pdev); +	if (!ds1wm_data->cell) +		return -ENODEV; +	plat = dev_get_platdata(&pdev->dev); +	if (!plat) +		return -ENODEV;  	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -	if (!res) { -		ret = -ENXIO; -		goto err1; -	} +	if (!res) +		return -ENXIO;  	ds1wm_data->irq = res->start; -	ds1wm_data->active_high = plat->active_high; +	ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); +	ds1wm_data->reset_recover_delay = plat->reset_recover_delay;  	if (res->flags & IORESOURCE_IRQ_HIGHEDGE) -		set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); +		irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING);  	if (res->flags & IORESOURCE_IRQ_LOWEDGE) -		set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); +		irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); -	ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED, -			  "ds1wm", ds1wm_data); +	ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, +			IRQF_SHARED, "ds1wm", ds1wm_data);  	if (ret) -		goto err1; +		return ret;  	ds1wm_up(ds1wm_data); @@ -388,17 +508,12 @@ static int ds1wm_probe(struct platform_device *pdev)  	ret = w1_add_master_device(&ds1wm_master);  	if (ret) -		goto err2; +		goto err;  	return 0; -err2: +err:  	ds1wm_down(ds1wm_data); -	free_irq(ds1wm_data->irq, ds1wm_data); -err1: -	iounmap(ds1wm_data->map); -err0: -	kfree(ds1wm_data);  	return ret;  } @@ -432,9 +547,6 @@ static int ds1wm_remove(struct platform_device *pdev)  	w1_remove_master_device(&ds1wm_master);  	ds1wm_down(ds1wm_data); -	free_irq(ds1wm_data->irq, ds1wm_data); -	iounmap(ds1wm_data->map); -	kfree(ds1wm_data);  	return 0;  } @@ -465,5 +577,6 @@ module_exit(ds1wm_exit);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " -	      "Matt Reimer <mreimer@vpop.net>"); +	"Matt Reimer <mreimer@vpop.net>," +	"Jean-Francois Dagenais <dagenaisj@sonatest.com>");  MODULE_DESCRIPTION("DS1WM w1 busmaster driver"); diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e5f74416d4b..e033491fe30 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -51,10 +51,10 @@   * The top 4 bits always read 0.   * To write, the top nibble must be the 1's compl. of the low nibble.   */ -#define DS2482_REG_CFG_1WS		0x08 -#define DS2482_REG_CFG_SPU		0x04 -#define DS2482_REG_CFG_PPM		0x02 -#define DS2482_REG_CFG_APU		0x01 +#define DS2482_REG_CFG_1WS		0x08	/* 1-wire speed */ +#define DS2482_REG_CFG_SPU		0x04	/* strong pull-up */ +#define DS2482_REG_CFG_PPM		0x02	/* presence pulse masking */ +#define DS2482_REG_CFG_APU		0x01	/* active pull-up */  /** @@ -132,6 +132,17 @@ struct ds2482_data {  /** + * Helper to calculate values for configuration register + * @param conf the raw config value + * @return the value w/ complements that can be written to register + */ +static inline u8 ds2482_calculate_config(u8 conf) +{ +	return conf | ((~conf & 0x0f) << 4); +} + + +/**   * Sets the read pointer.   * @param pdev		The ds2482 client pointer   * @param read_ptr	see DS2482_PTR_CODE_xxx above @@ -399,7 +410,7 @@ static u8 ds2482_w1_reset_bus(void *data)  		/* If the chip did reset since detect, re-config it */  		if (err & DS2482_REG_STS_RST)  			ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, -					     0xF0); +					     ds2482_calculate_config(0x00));  	}  	mutex_unlock(&pdev->access_lock); @@ -407,6 +418,32 @@ static u8 ds2482_w1_reset_bus(void *data)  	return retval;  } +static u8 ds2482_w1_set_pullup(void *data, int delay) +{ +	struct ds2482_w1_chan *pchan = data; +	struct ds2482_data    *pdev = pchan->pdev; +	u8 retval = 1; + +	/* if delay is non-zero activate the pullup, +	 * the strong pullup will be automatically deactivated +	 * by the master, so do not explicitly deactive it +	 */ +	if (delay) { +		/* both waits are crucial, otherwise devices might not be +		 * powered long enough, causing e.g. a w1_therm sensor to +		 * provide wrong conversion results +		 */ +		ds2482_wait_1wire_idle(pdev); +		/* note: it seems like both SPU and APU have to be set! */ +		retval = ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, +			ds2482_calculate_config(DS2482_REG_CFG_SPU | +						DS2482_REG_CFG_APU)); +		ds2482_wait_1wire_idle(pdev); +	} + +	return retval; +} +  static int ds2482_probe(struct i2c_client *client,  			const struct i2c_device_id *id) @@ -452,7 +489,8 @@ static int ds2482_probe(struct i2c_client *client,  		data->w1_count = 8;  	/* Set all config items to 0 (off) */ -	ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, 0xF0); +	ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, +		ds2482_calculate_config(0x00));  	mutex_init(&data->access_lock); @@ -468,6 +506,7 @@ static int ds2482_probe(struct i2c_client *client,  		data->w1_ch[idx].w1_bm.touch_bit  = ds2482_w1_touch_bit;  		data->w1_ch[idx].w1_bm.triplet    = ds2482_w1_triplet;  		data->w1_ch[idx].w1_bm.reset_bus  = ds2482_w1_reset_bus; +		data->w1_ch[idx].w1_bm.set_pullup = ds2482_w1_set_pullup;  		err = w1_add_master_device(&data->w1_ch[idx].w1_bm);  		if (err) { @@ -505,19 +544,8 @@ static int ds2482_remove(struct i2c_client *client)  	return 0;  } -static int __init sensors_ds2482_init(void) -{ -	return i2c_add_driver(&ds2482_driver); -} - -static void __exit sensors_ds2482_exit(void) -{ -	i2c_del_driver(&ds2482_driver); -} +module_i2c_driver(ds2482_driver);  MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");  MODULE_DESCRIPTION("DS2482 driver");  MODULE_LICENSE("GPL"); - -module_init(sensors_ds2482_init); -module_exit(sensors_ds2482_exit); diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 02bf7bf7160..7404ad3062b 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1,7 +1,7 @@  /* - *	dscore.c + *	ds2490.c  USB to one wire bridge   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,10 @@  #include "../w1_int.h"  #include "../w1.h" +/* USB Standard */ +/* USB Control request vendor type */ +#define VENDOR				0x40 +  /* COMMAND TYPE CODES */  #define CONTROL_CMD			0x00  #define COMM_CMD			0x01 @@ -107,6 +111,8 @@  #define ST_HALT				0x10  /* DS2490 is currently halted */  #define ST_IDLE				0x20  /* DS2490 is currently idle */  #define ST_EPOF				0x80 +/* Status transfer size, 16 bytes status, 16 byte result flags */ +#define ST_SIZE				0x20  /* Result Register flags */  #define RR_DETECT			0xA5 /* New device detected */ @@ -198,7 +204,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index)  	int err;  	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), -			CONTROL_CMD, 0x40, value, index, NULL, 0, 1000); +			CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000);  	if (err < 0) {  		printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n",  				value, index, err); @@ -213,7 +219,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index)  	int err;  	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), -			MODE_CMD, 0x40, value, index, NULL, 0, 1000); +			MODE_CMD, VENDOR, value, index, NULL, 0, 1000);  	if (err < 0) {  		printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n",  				value, index, err); @@ -228,7 +234,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index)  	int err;  	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), -			COMM_CMD, 0x40, value, index, NULL, 0, 1000); +			COMM_CMD, VENDOR, value, index, NULL, 0, 1000);  	if (err < 0) {  		printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n",  				value, index, err); @@ -246,7 +252,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,  	memset(st, 0, sizeof(*st));  	count = 0; -	err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100); +	err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, +		dev->ep[EP_STATUS]), buf, size, &count, 100);  	if (err < 0) {  		printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err);  		return err; @@ -353,7 +360,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)  	err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),  				buf, size, &count, 1000);  	if (err < 0) { -		u8 buf[0x20]; +		u8 buf[ST_SIZE];  		int count;  		printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); @@ -398,7 +405,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit)  {  	struct ds_status st;  	int count = 0, err = 0; -	u8 buf[0x20]; +	u8 buf[ST_SIZE];  	do {  		err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -450,10 +457,11 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)  static int ds_wait_status(struct ds_device *dev, struct ds_status *st)  { -	u8 buf[0x20]; +	u8 buf[ST_SIZE];  	int err, count = 0;  	do { +		st->status = 0;  		err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));  #if 0  		if (err >= 0) { @@ -464,7 +472,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st)  			printk("\n");  		}  #endif -	} while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100); +	} while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100);  	if (err >= 16 && st->status & ST_EPOF) {  		printk(KERN_INFO "Resetting device after ST_EPOF.\n"); @@ -690,37 +698,106 @@ static int ds_write_block(struct ds_device *dev, u8 *buf, int len)  	return !(err == len);  } -#if 0 - -static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search) +static void ds9490r_search(void *data, struct w1_master *master, +	u8 search_type, w1_slave_found_callback callback)  { +	/* When starting with an existing id, the first id returned will +	 * be that device (if it is still on the bus most likely). +	 * +	 * If the number of devices found is less than or equal to the +	 * search_limit, that number of IDs will be returned.  If there are +	 * more, search_limit IDs will be returned followed by a non-zero +	 * discrepency value. +	 */ +	struct ds_device *dev = data;  	int err;  	u16 value, index;  	struct ds_status st; +	u8 st_buf[ST_SIZE]; +	int search_limit; +	int found = 0; +	int i; -	memset(buf, 0, sizeof(buf)); +	/* DS18b20 spec, 13.16 ms per device, 75 per second, sleep for +	 * discovering 8 devices (1 bulk transfer and 1/2 FIFO size) at a time. +	 */ +	const unsigned long jtime = msecs_to_jiffies(1000*8/75); +	/* FIFO 128 bytes, bulk packet size 64, read a multiple of the +	 * packet size. +	 */ +	u64 buf[2*64/8]; -	err = ds_send_data(ds_dev, (unsigned char *)&init, 8); -	if (err) -		return err; +	mutex_lock(&master->bus_mutex); -	ds_wait_status(ds_dev, &st); +	/* address to start searching at */ +	if (ds_send_data(dev, (u8 *)&master->search_id, 8) < 0) +		goto search_out; +	master->search_id = 0; -	value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS; -	index = (conditional_search ? 0xEC : 0xF0) | (id_number << 8); -	err = ds_send_control(ds_dev, value, index); -	if (err) -		return err; +	value = COMM_SEARCH_ACCESS | COMM_IM | COMM_RST | COMM_SM | COMM_F | +		COMM_RTS; +	search_limit = master->max_slave_count; +	if (search_limit > 255) +		search_limit = 0; +	index = search_type | (search_limit << 8); +	if (ds_send_control(dev, value, index) < 0) +		goto search_out; -	ds_wait_status(ds_dev, &st); +	do { +		schedule_timeout(jtime); -	err = ds_recv_data(ds_dev, (unsigned char *)buf, 8*id_number); -	if (err < 0) -		return err; +		if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < +			sizeof(st)) { +			break; +		} -	return err/8; +		if (st.data_in_buffer_status) { +			/* Bulk in can receive partial ids, but when it does +			 * they fail crc and will be discarded anyway. +			 * That has only been seen when status in buffer +			 * is 0 and bulk is read anyway, so don't read +			 * bulk without first checking if status says there +			 * is data to read. +			 */ +			err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); +			if (err < 0) +				break; +			for (i = 0; i < err/8; ++i) { +				++found; +				if (found <= search_limit) +					callback(master, buf[i]); +				/* can't know if there will be a discrepancy +				 * value after until the next id */ +				if (found == search_limit) +					master->search_id = buf[i]; +			} +		} + +		if (test_bit(W1_ABORT_SEARCH, &master->flags)) +			break; +	} while (!(st.status & (ST_IDLE | ST_HALT))); + +	/* only continue the search if some weren't found */ +	if (found <= search_limit) { +		master->search_id = 0; +	} else if (!test_bit(W1_WARN_MAX_COUNT, &master->flags)) { +		/* Only max_slave_count will be scanned in a search, +		 * but it will start where it left off next search +		 * until all ids are identified and then it will start +		 * over.  A continued search will report the previous +		 * last id as the first id (provided it is still on the +		 * bus). +		 */ +		dev_info(&dev->udev->dev, "%s: max_slave_count %d reached, " +			"will continue next search.\n", __func__, +			master->max_slave_count); +		set_bit(W1_WARN_MAX_COUNT, &master->flags); +	} +search_out: +	mutex_unlock(&master->bus_mutex);  } +#if 0  static int ds_match_access(struct ds_device *dev, u64 init)  {  	int err; @@ -894,6 +971,7 @@ static int ds_w1_init(struct ds_device *dev)  	dev->master.write_block	= &ds9490r_write_block;  	dev->master.reset_bus	= &ds9490r_reset;  	dev->master.set_pullup	= &ds9490r_set_pullup; +	dev->master.search	= &ds9490r_search;  	return w1_add_master_device(&dev->master);  } @@ -910,15 +988,13 @@ static int ds_probe(struct usb_interface *intf,  	struct usb_endpoint_descriptor *endpoint;  	struct usb_host_interface *iface_desc;  	struct ds_device *dev; -	int i, err; +	int i, err, alt; -	dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL); +	dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL);  	if (!dev) {  		printk(KERN_INFO "Failed to allocate new DS9490R structure.\n");  		return -ENOMEM;  	} -	dev->spu_sleep = 0; -	dev->spu_bit = 0;  	dev->udev = usb_get_dev(udev);  	if (!dev->udev) {  		err = -ENOMEM; @@ -928,20 +1004,25 @@ static int ds_probe(struct usb_interface *intf,  	usb_set_intfdata(intf, dev); -	err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3); +	err = usb_reset_configuration(dev->udev);  	if (err) { -		printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n", -				intf->altsetting[0].desc.bInterfaceNumber, err); +		dev_err(&dev->udev->dev, +			"Failed to reset configuration: err=%d.\n", err);  		goto err_out_clear;  	} -	err = usb_reset_configuration(dev->udev); +	/* alternative 3, 1ms interrupt (greatly speeds search), 64 byte bulk */ +	alt = 3; +	err = usb_set_interface(dev->udev, +		intf->altsetting[alt].desc.bInterfaceNumber, alt);  	if (err) { -		printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err); +		dev_err(&dev->udev->dev, "Failed to set alternative setting %d " +			"for %d interface: err=%d.\n", alt, +			intf->altsetting[alt].desc.bInterfaceNumber, err);  		goto err_out_clear;  	} -	iface_desc = &intf->altsetting[0]; +	iface_desc = &intf->altsetting[alt];  	if (iface_desc->desc.bNumEndpoints != NUM_EP-1) {  		printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints);  		err = -EINVAL; @@ -1002,27 +1083,8 @@ static void ds_disconnect(struct usb_interface *intf)  	kfree(dev);  } -static int ds_init(void) -{ -	int err; - -	err = usb_register(&ds_driver); -	if (err) { -		printk(KERN_INFO "Failed to register DS9490R USB device: err=%d.\n", err); -		return err; -	} - -	return 0; -} - -static void ds_fini(void) -{ -	usb_deregister(&ds_driver); -} - -module_init(ds_init); -module_exit(ds_fini); +module_usb_driver(ds_driver);  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");  MODULE_DESCRIPTION("DS2490 USB <-> W1 bus master driver (DS9490*)"); diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index 1550431ccb6..d8667b0212d 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -1,7 +1,7 @@  /*   *	matrox_w1.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@   */  #include <asm/types.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/io.h>  #include <linux/delay.h> @@ -39,7 +39,7 @@  #include "../w1_log.h"  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");  MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over VGA DDC(matrox gpio).");  static struct pci_device_id matrox_w1_tbl[] = { @@ -48,14 +48,14 @@ static struct pci_device_id matrox_w1_tbl[] = {  };  MODULE_DEVICE_TABLE(pci, matrox_w1_tbl); -static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit matrox_w1_remove(struct pci_dev *); +static int matrox_w1_probe(struct pci_dev *, const struct pci_device_id *); +static void matrox_w1_remove(struct pci_dev *);  static struct pci_driver matrox_w1_pci_driver = {  	.name = "matrox_w1",  	.id_table = matrox_w1_tbl,  	.probe = matrox_w1_probe, -	.remove = __devexit_p(matrox_w1_remove), +	.remove = matrox_w1_remove,  };  /* @@ -152,7 +152,7 @@ static void matrox_w1_hw_init(struct matrox_device *dev)  	matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);  } -static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  {  	struct matrox_device *dev;  	int err; @@ -220,7 +220,7 @@ err_out_free_device:  	return err;  } -static void __devexit matrox_w1_remove(struct pci_dev *pdev) +static void matrox_w1_remove(struct pci_dev *pdev)  {  	struct matrox_device *dev = pci_get_drvdata(pdev); diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index a3b6a74c67a..a5df5e89d45 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -10,24 +10,16 @@   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. - *   */ -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h>  #include <linux/clk.h> -#include <linux/slab.h>  #include <linux/delay.h>  #include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h>  #include "../w1.h"  #include "../w1_int.h" -#include "../w1_log.h"  /* According to the mx27 Datasheet the reset procedure should take up to about   * 1350us. We set the timeout to 500*100us = 50ms for sure */ @@ -36,17 +28,16 @@  /*   * MXC W1 Register offsets   */ -#define MXC_W1_CONTROL          0x00 -#define MXC_W1_TIME_DIVIDER     0x02 -#define MXC_W1_RESET            0x04 -#define MXC_W1_COMMAND          0x06 -#define MXC_W1_TXRX             0x08 -#define MXC_W1_INTERRUPT        0x0A -#define MXC_W1_INTERRUPT_EN     0x0C +#define MXC_W1_CONTROL		0x00 +# define MXC_W1_CONTROL_RDST	BIT(3) +# define MXC_W1_CONTROL_WR(x)	BIT(5 - (x)) +# define MXC_W1_CONTROL_PST	BIT(6) +# define MXC_W1_CONTROL_RPP	BIT(7) +#define MXC_W1_TIME_DIVIDER	0x02 +#define MXC_W1_RESET		0x04  struct mxc_w1_device {  	void __iomem *regs; -	unsigned int clkdiv;  	struct clk *clk;  	struct w1_bus_master bus_master;  }; @@ -62,12 +53,12 @@ static u8 mxc_w1_ds2_reset_bus(void *data)  	unsigned int timeout_cnt = 0;  	struct mxc_w1_device *dev = data; -	__raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); +	writeb(MXC_W1_CONTROL_RPP, (dev->regs + MXC_W1_CONTROL));  	while (1) { -		reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); +		reg_val = readb(dev->regs + MXC_W1_CONTROL); -		if (((reg_val >> 7) & 0x1) == 0 || +		if (!(reg_val & MXC_W1_CONTROL_RPP) ||  		    timeout_cnt > MXC_W1_RESET_TIMEOUT)  			break;  		else @@ -75,7 +66,7 @@ static u8 mxc_w1_ds2_reset_bus(void *data)  		udelay(100);  	} -	return (reg_val >> 7) & 0x1; +	return !(reg_val & MXC_W1_CONTROL_PST);  }  /* @@ -91,121 +82,100 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)  					 * datasheet.  					 */ -	__raw_writeb((1 << (5 - bit)), ctrl_addr); +	writeb(MXC_W1_CONTROL_WR(bit), ctrl_addr);  	while (timeout_cnt--) { -		if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) +		if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit)))  			break;  		udelay(1);  	} -	return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; +	return !!(readb(ctrl_addr) & MXC_W1_CONTROL_RDST);  } -static int __devinit mxc_w1_probe(struct platform_device *pdev) +static int mxc_w1_probe(struct platform_device *pdev)  {  	struct mxc_w1_device *mdev; +	unsigned long clkrate;  	struct resource *res; -	int err = 0; - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) -		return -ENODEV; +	unsigned int clkdiv; +	int err; -	mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); +	mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device), +			    GFP_KERNEL);  	if (!mdev)  		return -ENOMEM; -	mdev->clk = clk_get(&pdev->dev, "owire"); -	if (!mdev->clk) { -		err = -ENODEV; -		goto failed_clk; -	} +	mdev->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(mdev->clk)) +		return PTR_ERR(mdev->clk); -	mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; +	clkrate = clk_get_rate(mdev->clk); +	if (clkrate < 10000000) +		dev_warn(&pdev->dev, +			 "Low clock frequency causes improper function\n"); -	res = request_mem_region(res->start, resource_size(res), -				"mxc_w1"); -	if (!res) { -		err = -EBUSY; -		goto failed_req; -	} +	clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000); +	clkrate /= clkdiv; +	if ((clkrate < 980000) || (clkrate > 1020000)) +		dev_warn(&pdev->dev, +			 "Incorrect time base frequency %lu Hz\n", clkrate); -	mdev->regs = ioremap(res->start, resource_size(res)); -	if (!mdev->regs) { -		printk(KERN_ERR "Cannot map frame buffer registers\n"); -		goto failed_ioremap; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	mdev->regs = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(mdev->regs)) +		return PTR_ERR(mdev->regs); -	clk_enable(mdev->clk); -	__raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); +	err = clk_prepare_enable(mdev->clk); +	if (err) +		return err; + +	writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER);  	mdev->bus_master.data = mdev;  	mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus;  	mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; -	err = w1_add_master_device(&mdev->bus_master); +	platform_set_drvdata(pdev, mdev); +	err = w1_add_master_device(&mdev->bus_master);  	if (err) -		goto failed_add; +		clk_disable_unprepare(mdev->clk); -	platform_set_drvdata(pdev, mdev); -	return 0; - -failed_add: -	iounmap(mdev->regs); -failed_ioremap: -	release_mem_region(res->start, resource_size(res)); -failed_req: -	clk_put(mdev->clk); -failed_clk: -	kfree(mdev);  	return err;  }  /*   * disassociate the w1 device from the driver   */ -static int __devexit mxc_w1_remove(struct platform_device *pdev) +static int mxc_w1_remove(struct platform_device *pdev)  {  	struct mxc_w1_device *mdev = platform_get_drvdata(pdev); -	struct resource *res; - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	w1_remove_master_device(&mdev->bus_master); -	iounmap(mdev->regs); -	release_mem_region(res->start, resource_size(res)); -	clk_disable(mdev->clk); -	clk_put(mdev->clk); - -	platform_set_drvdata(pdev, NULL); +	clk_disable_unprepare(mdev->clk);  	return 0;  } +static struct of_device_id mxc_w1_dt_ids[] = { +	{ .compatible = "fsl,imx21-owire" }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); +  static struct platform_driver mxc_w1_driver = {  	.driver = { -		   .name = "mxc_w1", +		.name = "mxc_w1", +		.owner = THIS_MODULE, +		.of_match_table = mxc_w1_dt_ids,  	},  	.probe = mxc_w1_probe,  	.remove = mxc_w1_remove,  }; - -static int __init mxc_w1_init(void) -{ -	return platform_driver_register(&mxc_w1_driver); -} - -static void mxc_w1_exit(void) -{ -	platform_driver_unregister(&mxc_w1_driver); -} - -module_init(mxc_w1_init); -module_exit(mxc_w1_exit); +module_platform_driver(mxc_w1_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Freescale Semiconductors Inc"); diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 3a7e9ff8a74..9900e8ec739 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -1,7 +1,7 @@  /*   * drivers/w1/masters/omap_hdq.c   * - * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2007,2012 Texas Instruments, Inc.   *   * This file is licensed under the terms of the GNU General Public License   * version 2. This program is licensed "as is" without any warranty of any @@ -14,12 +14,9 @@  #include <linux/interrupt.h>  #include <linux/slab.h>  #include <linux/err.h> -#include <linux/clk.h>  #include <linux/io.h>  #include <linux/sched.h> - -#include <asm/irq.h> -#include <mach/hardware.h> +#include <linux/pm_runtime.h>  #include "../w1.h"  #include "../w1_int.h" @@ -61,8 +58,6 @@ struct hdq_data {  	/* lock status update */  	struct  mutex		hdq_mutex;  	int			hdq_usecount; -	struct	clk		*hdq_ick; -	struct	clk		*hdq_fck;  	u8			hdq_irqstatus;  	/* device lock */  	spinlock_t		hdq_spinlock; @@ -74,7 +69,7 @@ struct hdq_data {  	int			init_trans;  }; -static int __devinit omap_hdq_probe(struct platform_device *pdev); +static int omap_hdq_probe(struct platform_device *pdev);  static int omap_hdq_remove(struct platform_device *pdev);  static struct platform_driver omap_hdq_driver = { @@ -102,20 +97,20 @@ static struct w1_bus_master omap_w1_master = {  /* HDQ register I/O routines */  static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset)  { -	return __raw_readb(hdq_data->hdq_base + offset); +	return __raw_readl(hdq_data->hdq_base + offset);  }  static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val)  { -	__raw_writeb(val, hdq_data->hdq_base + offset); +	__raw_writel(val, hdq_data->hdq_base + offset);  }  static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,  			u8 val, u8 mask)  { -	u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask) +	u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask)  			| (val & mask); -	__raw_writeb(new_val, hdq_data->hdq_base + offset); +	__raw_writel(new_val, hdq_data->hdq_base + offset);  	return new_val;  } @@ -180,6 +175,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)  		hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);  	if (ret == 0) {  		dev_dbg(hdq_data->dev, "TX wait elapsed\n"); +		ret = -ETIMEDOUT;  		goto out;  	} @@ -187,7 +183,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)  	/* check irqstatus */  	if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {  		dev_dbg(hdq_data->dev, "timeout waiting for" -			"TXCOMPLETE/RXCOMPLETE, %x", *status); +			" TXCOMPLETE/RXCOMPLETE, %x", *status);  		ret = -ETIMEDOUT;  		goto out;  	} @@ -198,7 +194,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)  			OMAP_HDQ_FLAG_CLEAR, &tmp_status);  	if (ret) {  		dev_dbg(hdq_data->dev, "timeout waiting GO bit" -			"return to zero, %x", tmp_status); +			" return to zero, %x", tmp_status);  	}  out: @@ -341,7 +337,7 @@ static int omap_hdq_break(struct hdq_data *hdq_data)  			&tmp_status);  	if (ret)  		dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" -			"return to zero, %x", tmp_status); +			" return to zero, %x", tmp_status);  out:  	mutex_unlock(&hdq_data->hdq_mutex); @@ -353,7 +349,6 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)  {  	int ret = 0;  	u8 status; -	unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;  	ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);  	if (ret < 0) { @@ -371,22 +366,20 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)  			OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO,  			OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);  		/* -		 * The RX comes immediately after TX. It -		 * triggers another interrupt before we -		 * sleep. So we have to wait for RXCOMPLETE bit. +		 * The RX comes immediately after TX.  		 */ -		while (!(hdq_data->hdq_irqstatus -			& OMAP_HDQ_INT_STATUS_RXCOMPLETE) -			&& time_before(jiffies, timeout)) { -			schedule_timeout_uninterruptible(1); -		} +		wait_event_timeout(hdq_wait_queue, +				   (hdq_data->hdq_irqstatus +				    & OMAP_HDQ_INT_STATUS_RXCOMPLETE), +				   OMAP_HDQ_TIMEOUT); +  		hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0,  			OMAP_HDQ_CTRL_STATUS_DIR);  		status = hdq_data->hdq_irqstatus;  		/* check irqstatus */  		if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {  			dev_dbg(hdq_data->dev, "timeout waiting for" -				"RXCOMPLETE, %x", status); +				" RXCOMPLETE, %x", status);  			ret = -ETIMEDOUT;  			goto out;  		} @@ -396,7 +389,7 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)  out:  	mutex_unlock(&hdq_data->hdq_mutex);  rtn: -	return 0; +	return ret;  } @@ -419,17 +412,8 @@ static int omap_hdq_get(struct hdq_data *hdq_data)  		hdq_data->hdq_usecount++;  		try_module_get(THIS_MODULE);  		if (1 == hdq_data->hdq_usecount) { -			if (clk_enable(hdq_data->hdq_ick)) { -				dev_dbg(hdq_data->dev, "Can not enable ick\n"); -				ret = -ENODEV; -				goto clk_err; -			} -			if (clk_enable(hdq_data->hdq_fck)) { -				dev_dbg(hdq_data->dev, "Can not enable fck\n"); -				clk_disable(hdq_data->hdq_ick); -				ret = -ENODEV; -				goto clk_err; -			} + +			pm_runtime_get_sync(hdq_data->dev);  			/* make sure HDQ is out of reset */  			if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & @@ -450,9 +434,6 @@ static int omap_hdq_get(struct hdq_data *hdq_data)  		}  	} -clk_err: -	clk_put(hdq_data->hdq_ick); -	clk_put(hdq_data->hdq_fck);  out:  	mutex_unlock(&hdq_data->hdq_mutex);  rtn: @@ -470,15 +451,13 @@ static int omap_hdq_put(struct hdq_data *hdq_data)  	if (0 == hdq_data->hdq_usecount) {  		dev_dbg(hdq_data->dev, "attempt to decrement use count" -			"when it is zero"); +			" when it is zero");  		ret = -EINVAL;  	} else {  		hdq_data->hdq_usecount--;  		module_put(THIS_MODULE); -		if (0 == hdq_data->hdq_usecount) { -			clk_disable(hdq_data->hdq_ick); -			clk_disable(hdq_data->hdq_fck); -		} +		if (0 == hdq_data->hdq_usecount) +			pm_runtime_put_sync(hdq_data->dev);  	}  	mutex_unlock(&hdq_data->hdq_mutex); @@ -540,12 +519,12 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)  	mutex_unlock(&hdq_data->hdq_mutex);  	ret = hdq_write_byte(hdq_data, byte, &status); -	if (ret == 0) { +	if (ret < 0) {  		dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);  		return;  	} -	/* Second write, data transfered. Release the module */ +	/* Second write, data transferred. Release the module */  	if (hdq_data->init_trans > 1) {  		omap_hdq_put(hdq_data);  		ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); @@ -556,72 +535,35 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)  		hdq_data->init_trans = 0;  		mutex_unlock(&hdq_data->hdq_mutex);  	} - -	return;  } -static int __devinit omap_hdq_probe(struct platform_device *pdev) +static int omap_hdq_probe(struct platform_device *pdev)  { +	struct device *dev = &pdev->dev;  	struct hdq_data *hdq_data;  	struct resource *res;  	int ret, irq;  	u8 rev; -	hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL); +	hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL);  	if (!hdq_data) {  		dev_dbg(&pdev->dev, "unable to allocate memory\n"); -		ret = -ENOMEM; -		goto err_kmalloc; +		return -ENOMEM;  	} -	hdq_data->dev = &pdev->dev; +	hdq_data->dev = dev;  	platform_set_drvdata(pdev, hdq_data);  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!res) { -		dev_dbg(&pdev->dev, "unable to get resource\n"); -		ret = -ENXIO; -		goto err_resource; -	} - -	hdq_data->hdq_base = ioremap(res->start, SZ_4K); -	if (!hdq_data->hdq_base) { -		dev_dbg(&pdev->dev, "ioremap failed\n"); -		ret = -EINVAL; -		goto err_ioremap; -	} - -	/* get interface & functional clock objects */ -	hdq_data->hdq_ick = clk_get(&pdev->dev, "ick"); -	hdq_data->hdq_fck = clk_get(&pdev->dev, "fck"); - -	if (IS_ERR(hdq_data->hdq_ick) || IS_ERR(hdq_data->hdq_fck)) { -		dev_dbg(&pdev->dev, "Can't get HDQ clock objects\n"); -		if (IS_ERR(hdq_data->hdq_ick)) { -			ret = PTR_ERR(hdq_data->hdq_ick); -			goto err_clk; -		} -		if (IS_ERR(hdq_data->hdq_fck)) { -			ret = PTR_ERR(hdq_data->hdq_fck); -			clk_put(hdq_data->hdq_ick); -			goto err_clk; -		} -	} +	hdq_data->hdq_base = devm_ioremap_resource(dev, res); +	if (IS_ERR(hdq_data->hdq_base)) +		return PTR_ERR(hdq_data->hdq_base);  	hdq_data->hdq_usecount = 0;  	mutex_init(&hdq_data->hdq_mutex); -	if (clk_enable(hdq_data->hdq_ick)) { -		dev_dbg(&pdev->dev, "Can not enable ick\n"); -		ret = -ENODEV; -		goto err_intfclk; -	} - -	if (clk_enable(hdq_data->hdq_fck)) { -		dev_dbg(&pdev->dev, "Can not enable fck\n"); -		ret = -ENODEV; -		goto err_fnclk; -	} +	pm_runtime_enable(&pdev->dev); +	pm_runtime_get_sync(&pdev->dev);  	rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION);  	dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", @@ -635,7 +577,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)  		goto err_irq;  	} -	ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data); +	ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data);  	if (ret < 0) {  		dev_dbg(&pdev->dev, "could not request irq\n");  		goto err_irq; @@ -643,9 +585,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)  	omap_hdq_break(hdq_data); -	/* don't clock the HDQ until it is needed */ -	clk_disable(hdq_data->hdq_ick); -	clk_disable(hdq_data->hdq_fck); +	pm_runtime_put_sync(&pdev->dev);  	omap_w1_master.data = hdq_data; @@ -657,28 +597,12 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)  	return 0; -err_w1:  err_irq: -	clk_disable(hdq_data->hdq_fck); - -err_fnclk: -	clk_disable(hdq_data->hdq_ick); - -err_intfclk: -	clk_put(hdq_data->hdq_ick); -	clk_put(hdq_data->hdq_fck); - -err_clk: -	iounmap(hdq_data->hdq_base); - -err_ioremap: -err_resource: -	platform_set_drvdata(pdev, NULL); -	kfree(hdq_data); +	pm_runtime_put_sync(&pdev->dev); +err_w1: +	pm_runtime_disable(&pdev->dev); -err_kmalloc:  	return ret; -  }  static int omap_hdq_remove(struct platform_device *pdev) @@ -696,29 +620,12 @@ static int omap_hdq_remove(struct platform_device *pdev)  	mutex_unlock(&hdq_data->hdq_mutex);  	/* remove module dependency */ -	clk_put(hdq_data->hdq_ick); -	clk_put(hdq_data->hdq_fck); -	free_irq(INT_24XX_HDQ_IRQ, hdq_data); -	platform_set_drvdata(pdev, NULL); -	iounmap(hdq_data->hdq_base); -	kfree(hdq_data); +	pm_runtime_disable(&pdev->dev);  	return 0;  } -static int __init -omap_hdq_init(void) -{ -	return platform_driver_register(&omap_hdq_driver); -} -module_init(omap_hdq_init); - -static void __exit -omap_hdq_exit(void) -{ -	platform_driver_unregister(&omap_hdq_driver); -} -module_exit(omap_hdq_exit); +module_platform_driver(omap_hdq_driver);  module_param(w1_id, int, S_IRUSR);  MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection"); diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index fcbe742188a..1d111e56c8c 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -13,11 +13,35 @@  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/w1-gpio.h> +#include <linux/gpio.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/delay.h>  #include "../w1.h"  #include "../w1_int.h" -#include <asm/gpio.h> +static u8 w1_gpio_set_pullup(void *data, int delay) +{ +	struct w1_gpio_platform_data *pdata = data; + +	if (delay) { +		pdata->pullup_duration = delay; +	} else { +		if (pdata->pullup_duration) { +			gpio_direction_output(pdata->pin, 1); + +			msleep(pdata->pullup_duration); + +			gpio_direction_input(pdata->pin); +		} +		pdata->pullup_duration = 0; +	} + +	return 0; +}  static void w1_gpio_write_bit_dir(void *data, u8 bit)  { @@ -43,22 +67,91 @@ static u8 w1_gpio_read_bit(void *data)  	return gpio_get_value(pdata->pin) ? 1 : 0;  } -static int __init w1_gpio_probe(struct platform_device *pdev) +#if defined(CONFIG_OF) +static struct of_device_id w1_gpio_dt_ids[] = { +	{ .compatible = "w1-gpio" }, +	{} +}; +MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); +#endif + +static int w1_gpio_probe_dt(struct platform_device *pdev) +{ +	struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); +	struct device_node *np = pdev->dev.of_node; +	int gpio; + +	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return -ENOMEM; + +	if (of_get_property(np, "linux,open-drain", NULL)) +		pdata->is_open_drain = 1; + +	gpio = of_get_gpio(np, 0); +	if (gpio < 0) { +		if (gpio != -EPROBE_DEFER) +			dev_err(&pdev->dev, +					"Failed to parse gpio property for data pin (%d)\n", +					gpio); + +		return gpio; +	} +	pdata->pin = gpio; + +	gpio = of_get_gpio(np, 1); +	if (gpio == -EPROBE_DEFER) +		return gpio; +	/* ignore other errors as the pullup gpio is optional */ +	pdata->ext_pullup_enable_pin = gpio; + +	pdev->dev.platform_data = pdata; + +	return 0; +} + +static int w1_gpio_probe(struct platform_device *pdev)  {  	struct w1_bus_master *master; -	struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; +	struct w1_gpio_platform_data *pdata;  	int err; -	if (!pdata) +	if (of_have_populated_dt()) { +		err = w1_gpio_probe_dt(pdev); +		if (err < 0) +			return err; +	} + +	pdata = dev_get_platdata(&pdev->dev); + +	if (!pdata) { +		dev_err(&pdev->dev, "No configuration data\n");  		return -ENXIO; +	} -	master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL); -	if (!master) +	master = devm_kzalloc(&pdev->dev, sizeof(struct w1_bus_master), +			GFP_KERNEL); +	if (!master) { +		dev_err(&pdev->dev, "Out of memory\n");  		return -ENOMEM; +	} -	err = gpio_request(pdata->pin, "w1"); -	if (err) -		goto free_master; +	err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); +	if (err) { +		dev_err(&pdev->dev, "gpio_request (pin) failed\n"); +		return err; +	} + +	if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { +		err = devm_gpio_request_one(&pdev->dev, +				pdata->ext_pullup_enable_pin, GPIOF_INIT_LOW, +				"w1 pullup"); +		if (err < 0) { +			dev_err(&pdev->dev, "gpio_request_one " +					"(ext_pullup_enable_pin) failed\n"); +			return err; +		} +	}  	master->data = pdata;  	master->read_bit = w1_gpio_read_bit; @@ -69,38 +162,38 @@ static int __init w1_gpio_probe(struct platform_device *pdev)  	} else {  		gpio_direction_input(pdata->pin);  		master->write_bit = w1_gpio_write_bit_dir; +		master->set_pullup = w1_gpio_set_pullup;  	}  	err = w1_add_master_device(master); -	if (err) -		goto free_gpio; +	if (err) { +		dev_err(&pdev->dev, "w1_add_master device failed\n"); +		return err; +	}  	if (pdata->enable_external_pullup)  		pdata->enable_external_pullup(1); +	if (gpio_is_valid(pdata->ext_pullup_enable_pin)) +		gpio_set_value(pdata->ext_pullup_enable_pin, 1); +  	platform_set_drvdata(pdev, master);  	return 0; - - free_gpio: -	gpio_free(pdata->pin); - free_master: -	kfree(master); - -	return err;  } -static int __exit w1_gpio_remove(struct platform_device *pdev) +static int w1_gpio_remove(struct platform_device *pdev)  {  	struct w1_bus_master *master = platform_get_drvdata(pdev); -	struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; +	struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);  	if (pdata->enable_external_pullup)  		pdata->enable_external_pullup(0); +	if (gpio_is_valid(pdata->ext_pullup_enable_pin)) +		gpio_set_value(pdata->ext_pullup_enable_pin, 0); +  	w1_remove_master_device(master); -	gpio_free(pdata->pin); -	kfree(master);  	return 0;  } @@ -109,7 +202,7 @@ static int __exit w1_gpio_remove(struct platform_device *pdev)  static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state)  { -	struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; +	struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);  	if (pdata->enable_external_pullup)  		pdata->enable_external_pullup(0); @@ -119,7 +212,7 @@ static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state)  static int w1_gpio_resume(struct platform_device *pdev)  { -	struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; +	struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);  	if (pdata->enable_external_pullup)  		pdata->enable_external_pullup(1); @@ -136,24 +229,15 @@ static struct platform_driver w1_gpio_driver = {  	.driver = {  		.name	= "w1-gpio",  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(w1_gpio_dt_ids),  	}, -	.remove	= __exit_p(w1_gpio_remove), +	.probe = w1_gpio_probe, +	.remove	= w1_gpio_remove,  	.suspend = w1_gpio_suspend,  	.resume = w1_gpio_resume,  }; -static int __init w1_gpio_init(void) -{ -	return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe); -} - -static void __exit w1_gpio_exit(void) -{ -	platform_driver_unregister(&w1_gpio_driver); -} - -module_init(w1_gpio_init); -module_exit(w1_gpio_exit); +module_platform_driver(w1_gpio_driver);  MODULE_DESCRIPTION("GPIO w1 bus master driver");  MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 1f51366417b..1cdce80b6ab 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -16,6 +16,39 @@ config W1_SLAVE_SMEM  	  Say Y here if you want to connect 1-wire  	  simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. +config W1_SLAVE_DS2408 +	tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" +	help +	  Say Y here if you want to use a 1-wire +	  DS2408 8-Channel Addressable Switch device support + +config W1_SLAVE_DS2408_READBACK +	bool "Read-back values written to DS2408's output register" +	depends on W1_SLAVE_DS2408 +	default y +	help +	  Enabling this will cause the driver to read back the values written +	  to the chip's output register in order to detect errors. + +	  This is slower but useful when debugging chips and/or busses. + +config W1_SLAVE_DS2413 +	tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" +	help +	  Say Y here if you want to use a 1-wire +	  DS2413 Dual Channel Addressable Switch device support + +config W1_SLAVE_DS2423 +	tristate "Counter 1-wire device (DS2423)" +	select CRC16 +	help +	  If you enable this you can read the counter values available +	  in the DS2423 chipset from the w1_slave file under the +	  sys file system. + +	  Say Y here if you want to use a 1-wire +	  counter family device (DS2423). +  config W1_SLAVE_DS2431  	tristate "1kb EEPROM family support (DS2431)"  	help @@ -39,7 +72,6 @@ config W1_SLAVE_DS2433_CRC  config W1_SLAVE_DS2760  	tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)" -	depends on W1  	help  	  If you enable this you will have the DS2760 battery monitor  	  chip support. @@ -50,9 +82,44 @@ config W1_SLAVE_DS2760  	  If you are unsure, say N. +config W1_SLAVE_DS2780 +	tristate "Dallas 2780 battery monitor chip" +	help +	  If you enable this you will have the DS2780 battery monitor +	  chip support. + +	  The battery monitor chip is used in many batteries/devices +	  as the one who is responsible for charging/discharging/monitoring +	  Li+ batteries. + +	  If you are unsure, say N. + +config W1_SLAVE_DS2781 +	tristate "Dallas 2781 battery monitor chip" +	help +	  If you enable this you will have the DS2781 battery monitor +	  chip support. + +	  The battery monitor chip is used in many batteries/devices +	  as the one who is responsible for charging/discharging/monitoring +	  Li+ batteries. + +	  If you are unsure, say N. + +config W1_SLAVE_DS28E04 +	tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)" +	select CRC16 +	help +	  If you enable this you will have the DS28E04-100 +	  chip support. + +	  Say Y here if you want to use a 1-wire +	  4kb EEPROM with PIO family device (DS28E04). + +	  If you are unsure, say N. +  config W1_SLAVE_BQ27000  	tristate "BQ27000 slave support" -	depends on W1  	help  	  Say Y here if you want to use a hdq  	  bq27000 slave support. diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index f1f51f19b12..06529f3157a 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,7 +4,13 @@  obj-$(CONFIG_W1_SLAVE_THERM)	+= w1_therm.o  obj-$(CONFIG_W1_SLAVE_SMEM)	+= w1_smem.o +obj-$(CONFIG_W1_SLAVE_DS2408)	+= w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2413)	+= w1_ds2413.o +obj-$(CONFIG_W1_SLAVE_DS2423)	+= w1_ds2423.o  obj-$(CONFIG_W1_SLAVE_DS2431)	+= w1_ds2431.o  obj-$(CONFIG_W1_SLAVE_DS2433)	+= w1_ds2433.o  obj-$(CONFIG_W1_SLAVE_DS2760)	+= w1_ds2760.o +obj-$(CONFIG_W1_SLAVE_DS2780)	+= w1_ds2780.o +obj-$(CONFIG_W1_SLAVE_DS2781)	+= w1_ds2781.o  obj-$(CONFIG_W1_SLAVE_BQ27000)	+= w1_bq27000.o +obj-$(CONFIG_W1_SLAVE_DS28E04)	+= w1_ds28e04.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 8f4c91f6c68..afbefed5f2c 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -15,6 +15,7 @@  #include <linux/types.h>  #include <linux/platform_device.h>  #include <linux/mutex.h> +#include <linux/power/bq27x00_battery.h>  #include "../w1.h"  #include "../w1_int.h" @@ -25,46 +26,39 @@  static int F_ID; -void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) -{ -	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); - -	if (!dev) { -		pr_info("Could not obtain slave dev ptr\n"); -		return; -	} - -	w1_write_8(sl->master, HDQ_CMD_WRITE | reg); -	w1_write_8(sl->master, buf); -} -EXPORT_SYMBOL(w1_bq27000_write); - -int w1_bq27000_read(struct device *dev, u8 reg) +static int w1_bq27000_read(struct device *dev, unsigned int reg)  {  	u8 val; -	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); - -	if (!dev) -		return 0; +	struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); +	mutex_lock(&sl->master->bus_mutex);  	w1_write_8(sl->master, HDQ_CMD_READ | reg);  	val = w1_read_8(sl->master); +	mutex_unlock(&sl->master->bus_mutex);  	return val;  } -EXPORT_SYMBOL(w1_bq27000_read); + +static struct bq27000_platform_data bq27000_battery_info = { +	.read   = w1_bq27000_read, +	.name   = "bq27000-battery", +};  static int w1_bq27000_add_slave(struct w1_slave *sl)  {  	int ret; -	int id = 1;  	struct platform_device *pdev; -	pdev = platform_device_alloc("bq27000-battery", id); +	pdev = platform_device_alloc("bq27000-battery", -1);  	if (!pdev) {  		ret = -ENOMEM;  		return ret;  	} +	ret = platform_device_add_data(pdev, +				       &bq27000_battery_info, +				       sizeof(bq27000_battery_info)); +	if (ret) +		goto pdev_add_failed;  	pdev->dev.parent = &sl->dev;  	ret = platform_device_add(pdev); @@ -76,7 +70,7 @@ static int w1_bq27000_add_slave(struct w1_slave *sl)  	goto success;  pdev_add_failed: -	platform_device_unregister(pdev); +	platform_device_put(pdev);  success:  	return ret;  } diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c new file mode 100644 index 00000000000..7dfa0e11688 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2408.c @@ -0,0 +1,366 @@ +/* + *	w1_ds2408.c - w1 family 29 (DS2408) driver + * + * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>"); +MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408)); + + +#define W1_F29_RETRIES		3 + +#define W1_F29_REG_LOGIG_STATE             0x88 /* R */ +#define W1_F29_REG_OUTPUT_LATCH_STATE      0x89 /* R */ +#define W1_F29_REG_ACTIVITY_LATCH_STATE    0x8A /* R */ +#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */ +#define W1_F29_REG_COND_SEARCH_POL_SELECT  0x8C /* RW */ +#define W1_F29_REG_CONTROL_AND_STATUS      0x8D /* RW */ + +#define W1_F29_FUNC_READ_PIO_REGS          0xF0 +#define W1_F29_FUNC_CHANN_ACCESS_READ      0xF5 +#define W1_F29_FUNC_CHANN_ACCESS_WRITE     0x5A +/* also used to write the control/status reg (0x8D): */ +#define W1_F29_FUNC_WRITE_COND_SEARCH_REG  0xCC +#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3 + +#define W1_F29_SUCCESS_CONFIRM_BYTE        0xAA + +static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) +{ +	u8 wrbuf[3]; +	dev_dbg(&sl->dev, +			"Reading with slave: %p, reg addr: %0#4x, buff addr: %p", +			sl, (unsigned int)address, buf); + +	if (!buf) +		return -EINVAL; + +	mutex_lock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex locked"); + +	if (w1_reset_select_slave(sl)) { +		mutex_unlock(&sl->master->bus_mutex); +		return -EIO; +	} + +	wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS; +	wrbuf[1] = address; +	wrbuf[2] = 0; +	w1_write_block(sl->master, wrbuf, 3); +	*buf = w1_read_8(sl->master); + +	mutex_unlock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex unlocked"); +	return 1; +} + +static ssize_t state_read(struct file *filp, struct kobject *kobj, +			  struct bin_attribute *bin_attr, char *buf, loff_t off, +			  size_t count) +{ +	dev_dbg(&kobj_to_w1_slave(kobj)->dev, +		"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", +		bin_attr->attr.name, kobj, (unsigned int)off, count, buf); +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); +} + +static ssize_t output_read(struct file *filp, struct kobject *kobj, +			   struct bin_attribute *bin_attr, char *buf, +			   loff_t off, size_t count) +{ +	dev_dbg(&kobj_to_w1_slave(kobj)->dev, +		"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", +		bin_attr->attr.name, kobj, (unsigned int)off, count, buf); +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), +					 W1_F29_REG_OUTPUT_LATCH_STATE, buf); +} + +static ssize_t activity_read(struct file *filp, struct kobject *kobj, +			     struct bin_attribute *bin_attr, char *buf, +			     loff_t off, size_t count) +{ +	dev_dbg(&kobj_to_w1_slave(kobj)->dev, +		"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", +		bin_attr->attr.name, kobj, (unsigned int)off, count, buf); +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), +					 W1_F29_REG_ACTIVITY_LATCH_STATE, buf); +} + +static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, +				     struct bin_attribute *bin_attr, char *buf, +				     loff_t off, size_t count) +{ +	dev_dbg(&kobj_to_w1_slave(kobj)->dev, +		"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", +		bin_attr->attr.name, kobj, (unsigned int)off, count, buf); +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), +		W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); +} + +static ssize_t cond_search_polarity_read(struct file *filp, +					 struct kobject *kobj, +					 struct bin_attribute *bin_attr, +					 char *buf, loff_t off, size_t count) +{ +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), +		W1_F29_REG_COND_SEARCH_POL_SELECT, buf); +} + +static ssize_t status_control_read(struct file *filp, struct kobject *kobj, +				   struct bin_attribute *bin_attr, char *buf, +				   loff_t off, size_t count) +{ +	if (count != 1 || off != 0) +		return -EFAULT; +	return _read_reg(kobj_to_w1_slave(kobj), +		W1_F29_REG_CONTROL_AND_STATUS, buf); +} + +static ssize_t output_write(struct file *filp, struct kobject *kobj, +			    struct bin_attribute *bin_attr, char *buf, +			    loff_t off, size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	u8 w1_buf[3]; +	u8 readBack; +	unsigned int retries = W1_F29_RETRIES; + +	if (count != 1 || off != 0) +		return -EFAULT; + +	dev_dbg(&sl->dev, "locking mutex for write_output"); +	mutex_lock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex locked"); + +	if (w1_reset_select_slave(sl)) +		goto error; + +	while (retries--) { +		w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE; +		w1_buf[1] = *buf; +		w1_buf[2] = ~(*buf); +		w1_write_block(sl->master, w1_buf, 3); + +		readBack = w1_read_8(sl->master); + +		if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) { +			if (w1_reset_resume_command(sl->master)) +				goto error; +			/* try again, the slave is ready for a command */ +			continue; +		} + +#ifdef CONFIG_W1_SLAVE_DS2408_READBACK +		/* here the master could read another byte which +		   would be the PIO reg (the actual pin logic state) +		   since in this driver we don't know which pins are +		   in and outs, there's no value to read the state and +		   compare. with (*buf) so end this command abruptly: */ +		if (w1_reset_resume_command(sl->master)) +			goto error; + +		/* go read back the output latches */ +		/* (the direct effect of the write above) */ +		w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; +		w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE; +		w1_buf[2] = 0; +		w1_write_block(sl->master, w1_buf, 3); +		/* read the result of the READ_PIO_REGS command */ +		if (w1_read_8(sl->master) == *buf) +#endif +		{ +			/* success! */ +			mutex_unlock(&sl->master->bus_mutex); +			dev_dbg(&sl->dev, +				"mutex unlocked, retries:%d", retries); +			return 1; +		} +	} +error: +	mutex_unlock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + +	return -EIO; +} + + +/** + * Writing to the activity file resets the activity latches. + */ +static ssize_t activity_write(struct file *filp, struct kobject *kobj, +			      struct bin_attribute *bin_attr, char *buf, +			      loff_t off, size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	unsigned int retries = W1_F29_RETRIES; + +	if (count != 1 || off != 0) +		return -EFAULT; + +	mutex_lock(&sl->master->bus_mutex); + +	if (w1_reset_select_slave(sl)) +		goto error; + +	while (retries--) { +		w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES); +		if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) { +			mutex_unlock(&sl->master->bus_mutex); +			return 1; +		} +		if (w1_reset_resume_command(sl->master)) +			goto error; +	} + +error: +	mutex_unlock(&sl->master->bus_mutex); +	return -EIO; +} + +static ssize_t status_control_write(struct file *filp, struct kobject *kobj, +				    struct bin_attribute *bin_attr, char *buf, +				    loff_t off, size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	u8 w1_buf[4]; +	unsigned int retries = W1_F29_RETRIES; + +	if (count != 1 || off != 0) +		return -EFAULT; + +	mutex_lock(&sl->master->bus_mutex); + +	if (w1_reset_select_slave(sl)) +		goto error; + +	while (retries--) { +		w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG; +		w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; +		w1_buf[2] = 0; +		w1_buf[3] = *buf; + +		w1_write_block(sl->master, w1_buf, 4); +		if (w1_reset_resume_command(sl->master)) +			goto error; + +		w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; +		w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; +		w1_buf[2] = 0; + +		w1_write_block(sl->master, w1_buf, 3); +		if (w1_read_8(sl->master) == *buf) { +			/* success! */ +			mutex_unlock(&sl->master->bus_mutex); +			return 1; +		} +	} +error: +	mutex_unlock(&sl->master->bus_mutex); + +	return -EIO; +} + +/* + * This is a special sequence we must do to ensure the P0 output is not stuck + * in test mode. This is described in rev 2 of the ds2408's datasheet + * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under + * "APPLICATION INFORMATION/Power-up timing". + */ +static int w1_f29_disable_test_mode(struct w1_slave *sl) +{ +	int res; +	u8 magic[10] = {0x96, }; +	u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); + +	memcpy(&magic[1], &rn, 8); +	magic[9] = 0x3C; + +	mutex_lock(&sl->master->bus_mutex); + +	res = w1_reset_bus(sl->master); +	if (res) +		goto out; +	w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); + +	res = w1_reset_bus(sl->master); +out: +	mutex_unlock(&sl->master->bus_mutex); +	return res; +} + +static BIN_ATTR_RO(state, 1); +static BIN_ATTR_RW(output, 1); +static BIN_ATTR_RW(activity, 1); +static BIN_ATTR_RO(cond_search_mask, 1); +static BIN_ATTR_RO(cond_search_polarity, 1); +static BIN_ATTR_RW(status_control, 1); + +static struct bin_attribute *w1_f29_bin_attrs[] = { +	&bin_attr_state, +	&bin_attr_output, +	&bin_attr_activity, +	&bin_attr_cond_search_mask, +	&bin_attr_cond_search_polarity, +	&bin_attr_status_control, +	NULL, +}; + +static const struct attribute_group w1_f29_group = { +	.bin_attrs = w1_f29_bin_attrs, +}; + +static const struct attribute_group *w1_f29_groups[] = { +	&w1_f29_group, +	NULL, +}; + +static struct w1_family_ops w1_f29_fops = { +	.add_slave      = w1_f29_disable_test_mode, +	.groups		= w1_f29_groups, +}; + +static struct w1_family w1_family_29 = { +	.fid = W1_FAMILY_DS2408, +	.fops = &w1_f29_fops, +}; + +static int __init w1_f29_init(void) +{ +	return w1_register_family(&w1_family_29); +} + +static void __exit w1_f29_exit(void) +{ +	w1_unregister_family(&w1_family_29); +} + +module_init(w1_f29_init); +module_exit(w1_f29_exit); diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c new file mode 100644 index 00000000000..ee28fc1ff39 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2413.c @@ -0,0 +1,150 @@ +/* + * w1_ds2413.c - w1 family 3a (DS2413) driver + * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); +MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413)); + +#define W1_F3A_RETRIES                     3 +#define W1_F3A_FUNC_PIO_ACCESS_READ        0xF5 +#define W1_F3A_FUNC_PIO_ACCESS_WRITE       0x5A +#define W1_F3A_SUCCESS_CONFIRM_BYTE        0xAA + +static ssize_t state_read(struct file *filp, struct kobject *kobj, +			  struct bin_attribute *bin_attr, char *buf, loff_t off, +			  size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	dev_dbg(&sl->dev, +		"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", +		bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + +	if (off != 0) +		return 0; +	if (!buf) +		return -EINVAL; + +	mutex_lock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex locked"); + +	if (w1_reset_select_slave(sl)) { +		mutex_unlock(&sl->master->bus_mutex); +		return -EIO; +	} + +	w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); +	*buf = w1_read_8(sl->master); + +	mutex_unlock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex unlocked"); + +	/* check for correct complement */ +	if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) +		return -EIO; +	else +		return 1; +} + +static BIN_ATTR_RO(state, 1); + +static ssize_t output_write(struct file *filp, struct kobject *kobj, +			    struct bin_attribute *bin_attr, char *buf, +			    loff_t off, size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	u8 w1_buf[3]; +	unsigned int retries = W1_F3A_RETRIES; + +	if (count != 1 || off != 0) +		return -EFAULT; + +	dev_dbg(&sl->dev, "locking mutex for write_output"); +	mutex_lock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex locked"); + +	if (w1_reset_select_slave(sl)) +		goto error; + +	/* according to the DS2413 datasheet the most significant 6 bits +	   should be set to "1"s, so do it now */ +	*buf = *buf | 0xFC; + +	while (retries--) { +		w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; +		w1_buf[1] = *buf; +		w1_buf[2] = ~(*buf); +		w1_write_block(sl->master, w1_buf, 3); + +		if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { +			mutex_unlock(&sl->master->bus_mutex); +			dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); +			return 1; +		} +		if (w1_reset_resume_command(sl->master)) +			goto error; +	} + +error: +	mutex_unlock(&sl->master->bus_mutex); +	dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); +	return -EIO; +} + +static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1); + +static struct bin_attribute *w1_f3a_bin_attrs[] = { +	&bin_attr_state, +	&bin_attr_output, +	NULL, +}; + +static const struct attribute_group w1_f3a_group = { +	.bin_attrs = w1_f3a_bin_attrs, +}; + +static const struct attribute_group *w1_f3a_groups[] = { +	&w1_f3a_group, +	NULL, +}; + +static struct w1_family_ops w1_f3a_fops = { +	.groups		= w1_f3a_groups, +}; + +static struct w1_family w1_family_3a = { +	.fid = W1_FAMILY_DS2413, +	.fops = &w1_f3a_fops, +}; + +static int __init w1_f3a_init(void) +{ +	return w1_register_family(&w1_family_3a); +} + +static void __exit w1_f3a_exit(void) +{ +	w1_unregister_family(&w1_family_3a); +} + +module_init(w1_f3a_init); +module_exit(w1_f3a_exit); diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c new file mode 100644 index 00000000000..7e41b7d91fb --- /dev/null +++ b/drivers/w1/slaves/w1_ds2423.c @@ -0,0 +1,158 @@ +/* + *	w1_ds2423.c + * + * Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org> + * + * This driver will read and write the value of 4 counters to w1_slave file in + * sys filesystem. + * Inspired by the w1_therm and w1_ds2431 drivers. + * + * This program is free software; you can redistribute it and/or modify + * it under the therms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/crc16.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define CRC16_VALID	0xb001 +#define CRC16_INIT	0 + +#define COUNTER_COUNT 4 +#define READ_BYTE_COUNT 42 + +static ssize_t w1_slave_show(struct device *device, +			     struct device_attribute *attr, char *out_buf) +{ +	struct w1_slave *sl = dev_to_w1_slave(device); +	struct w1_master *dev = sl->master; +	u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT]; +	u8 wrbuf[3]; +	int rom_addr; +	int read_byte_count; +	int result; +	ssize_t c; +	int ii; +	int p; +	int crc; + +	c		= PAGE_SIZE; +	rom_addr	= (12 << 5) + 31; +	wrbuf[0]	= 0xA5; +	wrbuf[1]	= rom_addr & 0xFF; +	wrbuf[2]	= rom_addr >> 8; +	mutex_lock(&dev->bus_mutex); +	if (!w1_reset_select_slave(sl)) { +		w1_write_block(dev, wrbuf, 3); +		read_byte_count = 0; +		for (p = 0; p < 4; p++) { +			/* +			 * 1 byte for first bytes in ram page read +			 * 4 bytes for counter +			 * 4 bytes for zero bits +			 * 2 bytes for crc +			 * 31 remaining bytes from the ram page +			 */ +			read_byte_count += w1_read_block(dev, +				rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT); +			for (ii = 0; ii < READ_BYTE_COUNT; ++ii) +				c -= snprintf(out_buf + PAGE_SIZE - c, +					c, "%02x ", +					rbuf[(p * READ_BYTE_COUNT) + ii]); +			if (read_byte_count != (p + 1) * READ_BYTE_COUNT) { +				dev_warn(device, +					"w1_counter_read() returned %u bytes " +					"instead of %d bytes wanted.\n", +					read_byte_count, +					READ_BYTE_COUNT); +				c -= snprintf(out_buf + PAGE_SIZE - c, +					c, "crc=NO\n"); +			} else { +				if (p == 0) { +					crc = crc16(CRC16_INIT, wrbuf, 3); +					crc = crc16(crc, rbuf, 11); +				} else { +					/* +					 * DS2423 calculates crc from all bytes +					 * read after the previous crc bytes. +					 */ +					crc = crc16(CRC16_INIT, +						(rbuf + 11) + +						((p - 1) * READ_BYTE_COUNT), +						READ_BYTE_COUNT); +				} +				if (crc == CRC16_VALID) { +					result = 0; +					for (ii = 4; ii > 0; ii--) { +						result <<= 8; +						result |= rbuf[(p * +							READ_BYTE_COUNT) + ii]; +					} +					c -= snprintf(out_buf + PAGE_SIZE - c, +						c, "crc=YES c=%d\n", result); +				} else { +					c -= snprintf(out_buf + PAGE_SIZE - c, +						c, "crc=NO\n"); +				} +			} +		} +	} else { +		c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error"); +	} +	mutex_unlock(&dev->bus_mutex); +	return PAGE_SIZE - c; +} + +static DEVICE_ATTR_RO(w1_slave); + +static struct attribute *w1_f1d_attrs[] = { +	&dev_attr_w1_slave.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(w1_f1d); + +static struct w1_family_ops w1_f1d_fops = { +	.groups		= w1_f1d_groups, +}; + +static struct w1_family w1_family_1d = { +	.fid = W1_COUNTER_DS2423, +	.fops = &w1_f1d_fops, +}; + +static int __init w1_f1d_init(void) +{ +	return w1_register_family(&w1_family_1d); +} + +static void __exit w1_f1d_exit(void) +{ +	w1_unregister_family(&w1_family_1d); +} + +module_init(w1_f1d_init); +module_exit(w1_f1d_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>"); +MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram"); +MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423)); diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 84e2410aec1..9c4ff9d28ad 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -96,9 +96,9 @@ static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf)  	return -1;  } -static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj, -			       struct bin_attribute *bin_attr, -			       char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, +			   struct bin_attribute *bin_attr, char *buf, +			   loff_t off, size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj);  	int todo = count; @@ -107,7 +107,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj,  	if (count == 0)  		return 0; -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  	/* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */  	while (todo > 0) { @@ -126,7 +126,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj,  		off += W1_F2D_READ_MAXLEN;  	} -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return count;  } @@ -202,9 +202,9 @@ retry:  	return 0;  } -static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj, -				struct bin_attribute *bin_attr, -				char *buf, loff_t off, size_t count) +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, +			    struct bin_attribute *bin_attr, char *buf, +			    loff_t off, size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj);  	int addr, len; @@ -214,7 +214,7 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj,  	if (count == 0)  		return 0; -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  	/* Can only write data in blocks of the size of the scratchpad */  	addr = off; @@ -259,34 +259,29 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj,  	}  out_up: -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return count;  } -static struct bin_attribute w1_f2d_bin_attr = { -	.attr = { -		.name = "eeprom", -		.mode = S_IRUGO | S_IWUSR, -	}, -	.size = W1_F2D_EEPROM_SIZE, -	.read = w1_f2d_read_bin, -	.write = w1_f2d_write_bin, +static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); + +static struct bin_attribute *w1_f2d_bin_attrs[] = { +	&bin_attr_eeprom, +	NULL,  }; -static int w1_f2d_add_slave(struct w1_slave *sl) -{ -	return sysfs_create_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); -} +static const struct attribute_group w1_f2d_group = { +	.bin_attrs = w1_f2d_bin_attrs, +}; -static void w1_f2d_remove_slave(struct w1_slave *sl) -{ -	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); -} +static const struct attribute_group *w1_f2d_groups[] = { +	&w1_f2d_group, +	NULL, +};  static struct w1_family_ops w1_f2d_fops = { -	.add_slave      = w1_f2d_add_slave, -	.remove_slave   = w1_f2d_remove_slave, +	.groups		= w1_f2d_groups,  };  static struct w1_family w1_family_2d = { @@ -310,3 +305,4 @@ module_exit(w1_f2d_fini);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>");  MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431)); diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 0f7b8f9c509..72319a968a9 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -29,6 +29,7 @@  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");  MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433));  #define W1_EEPROM_SIZE		512  #define W1_PAGE_COUNT		16 @@ -92,9 +93,9 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,  }  #endif	/* CONFIG_W1_SLAVE_DS2433_CRC */ -static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj, -			       struct bin_attribute *bin_attr, -			       char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, +			   struct bin_attribute *bin_attr, char *buf, +			   loff_t off, size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj);  #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -107,7 +108,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj,  	if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)  		return 0; -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -138,7 +139,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj,  #endif	/* CONFIG_W1_SLAVE_DS2433_CRC */  out_up: -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return count;  } @@ -206,9 +207,9 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)  	return 0;  } -static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj, -				struct bin_attribute *bin_attr, -				char *buf, loff_t off, size_t count) +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, +			    struct bin_attribute *bin_attr, char *buf, +			    loff_t off, size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj);  	int addr, len, idx; @@ -233,7 +234,7 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj,  	}  #endif	/* CONFIG_W1_SLAVE_DS2433_CRC */ -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  	/* Can only write data to one page at a time */  	idx = 0; @@ -251,24 +252,29 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj,  	}  out_up: -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return count;  } -static struct bin_attribute w1_f23_bin_attr = { -	.attr = { -		.name = "eeprom", -		.mode = S_IRUGO | S_IWUSR, -	}, -	.size = W1_EEPROM_SIZE, -	.read = w1_f23_read_bin, -	.write = w1_f23_write_bin, +static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static struct bin_attribute *w1_f23_bin_attributes[] = { +	&bin_attr_eeprom, +	NULL, +}; + +static const struct attribute_group w1_f23_group = { +	.bin_attrs = w1_f23_bin_attributes, +}; + +static const struct attribute_group *w1_f23_groups[] = { +	&w1_f23_group, +	NULL,  };  static int w1_f23_add_slave(struct w1_slave *sl)  { -	int err;  #ifdef CONFIG_W1_SLAVE_DS2433_CRC  	struct w1_f23_data *data; @@ -278,15 +284,7 @@ static int w1_f23_add_slave(struct w1_slave *sl)  	sl->family_data = data;  #endif	/* CONFIG_W1_SLAVE_DS2433_CRC */ - -	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); - -#ifdef CONFIG_W1_SLAVE_DS2433_CRC -	if (err) -		kfree(data); -#endif	/* CONFIG_W1_SLAVE_DS2433_CRC */ - -	return err; +	return 0;  }  static void w1_f23_remove_slave(struct w1_slave *sl) @@ -295,12 +293,12 @@ static void w1_f23_remove_slave(struct w1_slave *sl)  	kfree(sl->family_data);  	sl->family_data = NULL;  #endif	/* CONFIG_W1_SLAVE_DS2433_CRC */ -	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);  }  static struct w1_family_ops w1_f23_fops = {  	.add_slave      = w1_f23_add_slave,  	.remove_slave   = w1_f23_remove_slave, +	.groups		= w1_f23_groups,  };  static struct w1_family w1_family_23 = { diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index 483d4518091..65f90dccd60 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -31,7 +31,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,  	if (!dev)  		return 0; -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  	if (addr > DS2760_DATA_SIZE || addr < 0) {  		count = 0; @@ -54,7 +54,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,  	}  out: -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return count;  } @@ -76,14 +76,14 @@ static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd)  	if (!dev)  		return -EINVAL; -	mutex_lock(&sl->master->mutex); +	mutex_lock(&sl->master->bus_mutex);  	if (w1_reset_select_slave(sl) == 0) {  		w1_write_8(sl->master, cmd);  		w1_write_8(sl->master, addr);  	} -	mutex_unlock(&sl->master->mutex); +	mutex_unlock(&sl->master->bus_mutex);  	return 0;  } @@ -97,60 +97,31 @@ int w1_ds2760_recall_eeprom(struct device *dev, int addr)  	return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA);  } -static ssize_t w1_ds2760_read_bin(struct file *filp, struct kobject *kobj, -				  struct bin_attribute *bin_attr, -				  char *buf, loff_t off, size_t count) +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, +			     struct bin_attribute *bin_attr, char *buf, +			     loff_t off, size_t count)  {  	struct device *dev = container_of(kobj, struct device, kobj);  	return w1_ds2760_read(dev, buf, off, count);  } -static struct bin_attribute w1_ds2760_bin_attr = { -	.attr = { -		.name = "w1_slave", -		.mode = S_IRUGO, -	}, -	.size = DS2760_DATA_SIZE, -	.read = w1_ds2760_read_bin, -}; - -static DEFINE_IDR(bat_idr); -static DEFINE_MUTEX(bat_idr_lock); - -static int new_bat_id(void) -{ -	int ret; +static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE); -	while (1) { -		int id; - -		ret = idr_pre_get(&bat_idr, GFP_KERNEL); -		if (ret == 0) -			return -ENOMEM; - -		mutex_lock(&bat_idr_lock); -		ret = idr_get_new(&bat_idr, NULL, &id); -		mutex_unlock(&bat_idr_lock); +static struct bin_attribute *w1_ds2760_bin_attrs[] = { +	&bin_attr_w1_slave, +	NULL, +}; -		if (ret == 0) { -			ret = id & MAX_ID_MASK; -			break; -		} else if (ret == -EAGAIN) { -			continue; -		} else { -			break; -		} -	} +static const struct attribute_group w1_ds2760_group = { +	.bin_attrs = w1_ds2760_bin_attrs, +}; -	return ret; -} +static const struct attribute_group *w1_ds2760_groups[] = { +	&w1_ds2760_group, +	NULL, +}; -static void release_bat_id(int id) -{ -	mutex_lock(&bat_idr_lock); -	idr_remove(&bat_idr, id); -	mutex_unlock(&bat_idr_lock); -} +static DEFINE_IDA(bat_ida);  static int w1_ds2760_add_slave(struct w1_slave *sl)  { @@ -158,7 +129,7 @@ static int w1_ds2760_add_slave(struct w1_slave *sl)  	int id;  	struct platform_device *pdev; -	id = new_bat_id(); +	id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);  	if (id < 0) {  		ret = id;  		goto noid; @@ -175,19 +146,14 @@ static int w1_ds2760_add_slave(struct w1_slave *sl)  	if (ret)  		goto pdev_add_failed; -	ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); -	if (ret) -		goto bin_attr_failed; -  	dev_set_drvdata(&sl->dev, pdev);  	goto success; -bin_attr_failed:  pdev_add_failed: -	platform_device_unregister(pdev); +	platform_device_put(pdev);  pdev_alloc_failed: -	release_bat_id(id); +	ida_simple_remove(&bat_ida, id);  noid:  success:  	return ret; @@ -199,13 +165,13 @@ static void w1_ds2760_remove_slave(struct w1_slave *sl)  	int id = pdev->id;  	platform_device_unregister(pdev); -	release_bat_id(id); -	sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); +	ida_simple_remove(&bat_ida, id);  }  static struct w1_family_ops w1_ds2760_fops = {  	.add_slave    = w1_ds2760_add_slave,  	.remove_slave = w1_ds2760_remove_slave, +	.groups       = w1_ds2760_groups,  };  static struct w1_family w1_ds2760_family = { @@ -217,14 +183,14 @@ static int __init w1_ds2760_init(void)  {  	printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor "  	       " chip  - (c) 2004-2005, Szabolcs Gyurko\n"); -	idr_init(&bat_idr); +	ida_init(&bat_ida);  	return w1_register_family(&w1_ds2760_family);  }  static void __exit w1_ds2760_exit(void)  {  	w1_unregister_family(&w1_ds2760_family); -	idr_destroy(&bat_idr); +	ida_destroy(&bat_ida);  }  EXPORT_SYMBOL(w1_ds2760_read); @@ -238,3 +204,4 @@ module_exit(w1_ds2760_exit);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>");  MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760)); diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c new file mode 100644 index 00000000000..50e85f7929d --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.c @@ -0,0 +1,191 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2780.h" + +static int w1_ds2780_do_io(struct device *dev, char *buf, int addr, +			size_t count, int io) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + +	if (addr > DS2780_DATA_SIZE || addr < 0) +		return 0; + +	count = min_t(int, count, DS2780_DATA_SIZE - addr); + +	if (w1_reset_select_slave(sl) == 0) { +		if (io) { +			w1_write_8(sl->master, W1_DS2780_WRITE_DATA); +			w1_write_8(sl->master, addr); +			w1_write_block(sl->master, buf, count); +		} else { +			w1_write_8(sl->master, W1_DS2780_READ_DATA); +			w1_write_8(sl->master, addr); +			count = w1_read_block(sl->master, buf, count); +		} +	} + +	return count; +} + +int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, +			int io) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); +	int ret; + +	if (!dev) +		return -ENODEV; + +	mutex_lock(&sl->master->bus_mutex); + +	ret = w1_ds2780_do_io(dev, buf, addr, count, io); + +	mutex_unlock(&sl->master->bus_mutex); + +	return ret; +} +EXPORT_SYMBOL(w1_ds2780_io); + +int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + +	if (!dev) +		return -EINVAL; + +	mutex_lock(&sl->master->bus_mutex); + +	if (w1_reset_select_slave(sl) == 0) { +		w1_write_8(sl->master, cmd); +		w1_write_8(sl->master, addr); +	} + +	mutex_unlock(&sl->master->bus_mutex); +	return 0; +} +EXPORT_SYMBOL(w1_ds2780_eeprom_cmd); + +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, +			     struct bin_attribute *bin_attr, char *buf, +			     loff_t off, size_t count) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	return w1_ds2780_io(dev, buf, off, count, 0); +} + +static BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE); + +static struct bin_attribute *w1_ds2780_bin_attrs[] = { +	&bin_attr_w1_slave, +	NULL, +}; + +static const struct attribute_group w1_ds2780_group = { +	.bin_attrs = w1_ds2780_bin_attrs, +}; + +static const struct attribute_group *w1_ds2780_groups[] = { +	&w1_ds2780_group, +	NULL, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2780_add_slave(struct w1_slave *sl) +{ +	int ret; +	int id; +	struct platform_device *pdev; + +	id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); +	if (id < 0) { +		ret = id; +		goto noid; +	} + +	pdev = platform_device_alloc("ds2780-battery", id); +	if (!pdev) { +		ret = -ENOMEM; +		goto pdev_alloc_failed; +	} +	pdev->dev.parent = &sl->dev; + +	ret = platform_device_add(pdev); +	if (ret) +		goto pdev_add_failed; + +	dev_set_drvdata(&sl->dev, pdev); + +	return 0; + +pdev_add_failed: +	platform_device_put(pdev); +pdev_alloc_failed: +	ida_simple_remove(&bat_ida, id); +noid: +	return ret; +} + +static void w1_ds2780_remove_slave(struct w1_slave *sl) +{ +	struct platform_device *pdev = dev_get_drvdata(&sl->dev); +	int id = pdev->id; + +	platform_device_unregister(pdev); +	ida_simple_remove(&bat_ida, id); +} + +static struct w1_family_ops w1_ds2780_fops = { +	.add_slave    = w1_ds2780_add_slave, +	.remove_slave = w1_ds2780_remove_slave, +	.groups       = w1_ds2780_groups, +}; + +static struct w1_family w1_ds2780_family = { +	.fid = W1_FAMILY_DS2780, +	.fops = &w1_ds2780_fops, +}; + +static int __init w1_ds2780_init(void) +{ +	ida_init(&bat_ida); +	return w1_register_family(&w1_ds2780_family); +} + +static void __exit w1_ds2780_exit(void) +{ +	w1_unregister_family(&w1_ds2780_family); +	ida_destroy(&bat_ida); +} + +module_init(w1_ds2780_init); +module_exit(w1_ds2780_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780)); diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h new file mode 100644 index 00000000000..a1fba79eb1b --- /dev/null +++ b/drivers/w1/slaves/w1_ds2780.h @@ -0,0 +1,129 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _W1_DS2780_H +#define _W1_DS2780_H + +/* Function commands */ +#define W1_DS2780_READ_DATA		0x69 +#define W1_DS2780_WRITE_DATA		0x6C +#define W1_DS2780_COPY_DATA		0x48 +#define W1_DS2780_RECALL_DATA		0xB8 +#define W1_DS2780_LOCK			0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2780_STATUS_REG		0x01 +#define DS2780_RAAC_MSB_REG		0x02 +#define DS2780_RAAC_LSB_REG		0x03 +#define DS2780_RSAC_MSB_REG		0x04 +#define DS2780_RSAC_LSB_REG		0x05 +#define DS2780_RARC_REG			0x06 +#define DS2780_RSRC_REG			0x07 +#define DS2780_IAVG_MSB_REG		0x08 +#define DS2780_IAVG_LSB_REG		0x09 +#define DS2780_TEMP_MSB_REG		0x0A +#define DS2780_TEMP_LSB_REG		0x0B +#define DS2780_VOLT_MSB_REG		0x0C +#define DS2780_VOLT_LSB_REG		0x0D +#define DS2780_CURRENT_MSB_REG		0x0E +#define DS2780_CURRENT_LSB_REG		0x0F +#define DS2780_ACR_MSB_REG		0x10 +#define DS2780_ACR_LSB_REG		0x11 +#define DS2780_ACRL_MSB_REG		0x12 +#define DS2780_ACRL_LSB_REG		0x13 +#define DS2780_AS_REG			0x14 +#define DS2780_SFR_REG			0x15 +#define DS2780_FULL_MSB_REG		0x16 +#define DS2780_FULL_LSB_REG		0x17 +#define DS2780_AE_MSB_REG		0x18 +#define DS2780_AE_LSB_REG		0x19 +#define DS2780_SE_MSB_REG		0x1A +#define DS2780_SE_LSB_REG		0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2780_EEPROM_REG		0x1F +#define DS2780_EEPROM_BLOCK0_START	0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2780_EEPROM_BLOCK0_END	0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2780_EEPROM_BLOCK1_START	0x60 +#define DS2780_CONTROL_REG		0x60 +#define DS2780_AB_REG			0x61 +#define DS2780_AC_MSB_REG		0x62 +#define DS2780_AC_LSB_REG		0x63 +#define DS2780_VCHG_REG			0x64 +#define DS2780_IMIN_REG			0x65 +#define DS2780_VAE_REG			0x66 +#define DS2780_IAE_REG			0x67 +#define DS2780_AE_40_REG		0x68 +#define DS2780_RSNSP_REG		0x69 +#define DS2780_FULL_40_MSB_REG		0x6A +#define DS2780_FULL_40_LSB_REG		0x6B +#define DS2780_FULL_3040_SLOPE_REG	0x6C +#define DS2780_FULL_2030_SLOPE_REG	0x6D +#define DS2780_FULL_1020_SLOPE_REG	0x6E +#define DS2780_FULL_0010_SLOPE_REG	0x6F +#define DS2780_AE_3040_SLOPE_REG	0x70 +#define DS2780_AE_2030_SLOPE_REG	0x71 +#define DS2780_AE_1020_SLOPE_REG	0x72 +#define DS2780_AE_0010_SLOPE_REG	0x73 +#define DS2780_SE_3040_SLOPE_REG	0x74 +#define DS2780_SE_2030_SLOPE_REG	0x75 +#define DS2780_SE_1020_SLOPE_REG	0x76 +#define DS2780_SE_0010_SLOPE_REG	0x77 +#define DS2780_RSGAIN_MSB_REG		0x78 +#define DS2780_RSGAIN_LSB_REG		0x79 +#define DS2780_RSTC_REG			0x7A +#define DS2780_FRSGAIN_MSB_REG		0x7B +#define DS2780_FRSGAIN_LSB_REG		0x7C +#define DS2780_EEPROM_BLOCK1_END	0x7C +/* Register 0x7D - 0xFF Reserved */ + +/* Number of valid register addresses */ +#define DS2780_DATA_SIZE		0x80 + +/* Status register bits */ +#define DS2780_STATUS_REG_CHGTF		(1 << 7) +#define DS2780_STATUS_REG_AEF		(1 << 6) +#define DS2780_STATUS_REG_SEF		(1 << 5) +#define DS2780_STATUS_REG_LEARNF	(1 << 4) +/* Bit 3 Reserved */ +#define DS2780_STATUS_REG_UVF		(1 << 2) +#define DS2780_STATUS_REG_PORF		(1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2780_CONTROL_REG_UVEN		(1 << 6) +#define DS2780_CONTROL_REG_PMOD		(1 << 5) +#define DS2780_CONTROL_REG_RNAOP	(1 << 4) +/* Bit 0 - 3 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2780_SFR_REG_PIOSC		(1 << 0) + +/* EEPROM register bits */ +#define DS2780_EEPROM_REG_EEC		(1 << 7) +#define DS2780_EEPROM_REG_LOCK		(1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2780_EEPROM_REG_BL1		(1 << 1) +#define DS2780_EEPROM_REG_BL0		(1 << 0) + +extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, +			int io); +extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2780_H */ diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c new file mode 100644 index 00000000000..1eb98fb1688 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.c @@ -0,0 +1,189 @@ +/* + * 1-Wire implementation for the ds2781 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2780 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2781.h" + +static int w1_ds2781_do_io(struct device *dev, char *buf, int addr, +			size_t count, int io) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + +	if (addr > DS2781_DATA_SIZE || addr < 0) +		return 0; + +	count = min_t(int, count, DS2781_DATA_SIZE - addr); + +	if (w1_reset_select_slave(sl) == 0) { +		if (io) { +			w1_write_8(sl->master, W1_DS2781_WRITE_DATA); +			w1_write_8(sl->master, addr); +			w1_write_block(sl->master, buf, count); +		} else { +			w1_write_8(sl->master, W1_DS2781_READ_DATA); +			w1_write_8(sl->master, addr); +			count = w1_read_block(sl->master, buf, count); +		} +	} + +	return count; +} + +int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, +			int io) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); +	int ret; + +	if (!dev) +		return -ENODEV; + +	mutex_lock(&sl->master->bus_mutex); + +	ret = w1_ds2781_do_io(dev, buf, addr, count, io); + +	mutex_unlock(&sl->master->bus_mutex); + +	return ret; +} +EXPORT_SYMBOL(w1_ds2781_io); + +int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd) +{ +	struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + +	if (!dev) +		return -EINVAL; + +	mutex_lock(&sl->master->bus_mutex); + +	if (w1_reset_select_slave(sl) == 0) { +		w1_write_8(sl->master, cmd); +		w1_write_8(sl->master, addr); +	} + +	mutex_unlock(&sl->master->bus_mutex); +	return 0; +} +EXPORT_SYMBOL(w1_ds2781_eeprom_cmd); + +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, +			     struct bin_attribute *bin_attr, char *buf, +			     loff_t off, size_t count) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	return w1_ds2781_io(dev, buf, off, count, 0); +} + +static BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE); + +static struct bin_attribute *w1_ds2781_bin_attrs[] = { +	&bin_attr_w1_slave, +	NULL, +}; + +static const struct attribute_group w1_ds2781_group = { +	.bin_attrs = w1_ds2781_bin_attrs, +}; + +static const struct attribute_group *w1_ds2781_groups[] = { +	&w1_ds2781_group, +	NULL, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2781_add_slave(struct w1_slave *sl) +{ +	int ret; +	int id; +	struct platform_device *pdev; + +	id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); +	if (id < 0) { +		ret = id; +		goto noid; +	} + +	pdev = platform_device_alloc("ds2781-battery", id); +	if (!pdev) { +		ret = -ENOMEM; +		goto pdev_alloc_failed; +	} +	pdev->dev.parent = &sl->dev; + +	ret = platform_device_add(pdev); +	if (ret) +		goto pdev_add_failed; + +	dev_set_drvdata(&sl->dev, pdev); + +	return 0; + +pdev_add_failed: +	platform_device_put(pdev); +pdev_alloc_failed: +	ida_simple_remove(&bat_ida, id); +noid: +	return ret; +} + +static void w1_ds2781_remove_slave(struct w1_slave *sl) +{ +	struct platform_device *pdev = dev_get_drvdata(&sl->dev); +	int id = pdev->id; + +	platform_device_unregister(pdev); +	ida_simple_remove(&bat_ida, id); +} + +static struct w1_family_ops w1_ds2781_fops = { +	.add_slave    = w1_ds2781_add_slave, +	.remove_slave = w1_ds2781_remove_slave, +	.groups       = w1_ds2781_groups, +}; + +static struct w1_family w1_ds2781_family = { +	.fid = W1_FAMILY_DS2781, +	.fops = &w1_ds2781_fops, +}; + +static int __init w1_ds2781_init(void) +{ +	ida_init(&bat_ida); +	return w1_register_family(&w1_ds2781_family); +} + +static void __exit w1_ds2781_exit(void) +{ +	w1_unregister_family(&w1_ds2781_family); +	ida_destroy(&bat_ida); +} + +module_init(w1_ds2781_init); +module_exit(w1_ds2781_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781)); diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h new file mode 100644 index 00000000000..557dfb0b4f6 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.h @@ -0,0 +1,134 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _W1_DS2781_H +#define _W1_DS2781_H + +/* Function commands */ +#define W1_DS2781_READ_DATA		0x69 +#define W1_DS2781_WRITE_DATA		0x6C +#define W1_DS2781_COPY_DATA		0x48 +#define W1_DS2781_RECALL_DATA		0xB8 +#define W1_DS2781_LOCK			0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2781_STATUS			0x01 +#define DS2781_RAAC_MSB			0x02 +#define DS2781_RAAC_LSB			0x03 +#define DS2781_RSAC_MSB			0x04 +#define DS2781_RSAC_LSB			0x05 +#define DS2781_RARC			0x06 +#define DS2781_RSRC			0x07 +#define DS2781_IAVG_MSB			0x08 +#define DS2781_IAVG_LSB			0x09 +#define DS2781_TEMP_MSB			0x0A +#define DS2781_TEMP_LSB			0x0B +#define DS2781_VOLT_MSB			0x0C +#define DS2781_VOLT_LSB			0x0D +#define DS2781_CURRENT_MSB		0x0E +#define DS2781_CURRENT_LSB		0x0F +#define DS2781_ACR_MSB			0x10 +#define DS2781_ACR_LSB			0x11 +#define DS2781_ACRL_MSB			0x12 +#define DS2781_ACRL_LSB			0x13 +#define DS2781_AS			0x14 +#define DS2781_SFR			0x15 +#define DS2781_FULL_MSB			0x16 +#define DS2781_FULL_LSB			0x17 +#define DS2781_AE_MSB			0x18 +#define DS2781_AE_LSB			0x19 +#define DS2781_SE_MSB			0x1A +#define DS2781_SE_LSB			0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2781_EEPROM		0x1F +#define DS2781_EEPROM_BLOCK0_START	0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2781_EEPROM_BLOCK0_END	0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2781_EEPROM_BLOCK1_START	0x60 +#define DS2781_CONTROL			0x60 +#define DS2781_AB			0x61 +#define DS2781_AC_MSB			0x62 +#define DS2781_AC_LSB			0x63 +#define DS2781_VCHG			0x64 +#define DS2781_IMIN			0x65 +#define DS2781_VAE			0x66 +#define DS2781_IAE			0x67 +#define DS2781_AE_40			0x68 +#define DS2781_RSNSP			0x69 +#define DS2781_FULL_40_MSB		0x6A +#define DS2781_FULL_40_LSB		0x6B +#define DS2781_FULL_4_SLOPE		0x6C +#define DS2781_FULL_3_SLOPE		0x6D +#define DS2781_FULL_2_SLOPE		0x6E +#define DS2781_FULL_1_SLOPE		0x6F +#define DS2781_AE_4_SLOPE		0x70 +#define DS2781_AE_3_SLOPE		0x71 +#define DS2781_AE_2_SLOPE		0x72 +#define DS2781_AE_1_SLOPE		0x73 +#define DS2781_SE_4_SLOPE		0x74 +#define DS2781_SE_3_SLOPE		0x75 +#define DS2781_SE_2_SLOPE		0x76 +#define DS2781_SE_1_SLOPE		0x77 +#define DS2781_RSGAIN_MSB		0x78 +#define DS2781_RSGAIN_LSB		0x79 +#define DS2781_RSTC			0x7A +#define DS2781_COB			0x7B +#define DS2781_TBP34			0x7C +#define DS2781_TBP23			0x7D +#define DS2781_TBP12			0x7E +#define DS2781_EEPROM_BLOCK1_END	0x7F +/* Register 0x7D - 0xFF Reserved */ + +#define DS2781_FSGAIN_MSB		0xB0 +#define DS2781_FSGAIN_LSB		0xB1 + +/* Number of valid register addresses */ +#define DS2781_DATA_SIZE		0xB2 + +/* Status register bits */ +#define DS2781_STATUS_CHGTF		(1 << 7) +#define DS2781_STATUS_AEF		(1 << 6) +#define DS2781_STATUS_SEF		(1 << 5) +#define DS2781_STATUS_LEARNF		(1 << 4) +/* Bit 3 Reserved */ +#define DS2781_STATUS_UVF		(1 << 2) +#define DS2781_STATUS_PORF		(1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2781_CONTROL_NBEN		(1 << 7) +#define DS2781_CONTROL_UVEN		(1 << 6) +#define DS2781_CONTROL_PMOD		(1 << 5) +#define DS2781_CONTROL_RNAOP		(1 << 4) +#define DS1781_CONTROL_UVTH		(1 << 3) +/* Bit 0 - 2 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2781_SFR_PIOSC		(1 << 0) + +/* EEPROM register bits */ +#define DS2781_EEPROM_EEC		(1 << 7) +#define DS2781_EEPROM_LOCK		(1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2781_EEPROM_BL1		(1 << 1) +#define DS2781_EEPROM_BL0		(1 << 0) + +extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, +			int io); +extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2781_H */ diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c new file mode 100644 index 00000000000..365d6dff21d --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -0,0 +1,442 @@ +/* + *	w1_ds28e04.c - w1 family 1C (DS28E04) driver + * + * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/crc16.h> +#include <linux/uaccess.h> + +#define CRC16_INIT		0 +#define CRC16_VALID		0xb001 + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>"); +MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04)); + +/* Allow the strong pullup to be disabled, but default to enabled. + * If it was disabled a parasite powered device might not get the required + * current to copy the data from the scratchpad to EEPROM.  If it is enabled + * parasite powered devices have a better chance of getting the current + * required. + */ +static int w1_strong_pullup = 1; +module_param_named(strong_pullup, w1_strong_pullup, int, 0); + +/* enable/disable CRC checking on DS28E04-100 memory accesses */ +static char w1_enable_crccheck = 1; + +#define W1_EEPROM_SIZE		512 +#define W1_PAGE_COUNT		16 +#define W1_PAGE_SIZE		32 +#define W1_PAGE_BITS		5 +#define W1_PAGE_MASK		0x1F + +#define W1_F1C_READ_EEPROM	0xF0 +#define W1_F1C_WRITE_SCRATCH	0x0F +#define W1_F1C_READ_SCRATCH	0xAA +#define W1_F1C_COPY_SCRATCH	0x55 +#define W1_F1C_ACCESS_WRITE	0x5A + +#define W1_1C_REG_LOGIC_STATE	0x220 + +struct w1_f1C_data { +	u8	memory[W1_EEPROM_SIZE]; +	u32	validcrc; +}; + +/** + * Check the file size bounds and adjusts count as needed. + * This would not be needed if the file size didn't reset to 0 after a write. + */ +static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size) +{ +	if (off > size) +		return 0; + +	if ((off + count) > size) +		return size - off; + +	return count; +} + +static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data, +				int block) +{ +	u8	wrbuf[3]; +	int	off = block * W1_PAGE_SIZE; + +	if (data->validcrc & (1 << block)) +		return 0; + +	if (w1_reset_select_slave(sl)) { +		data->validcrc = 0; +		return -EIO; +	} + +	wrbuf[0] = W1_F1C_READ_EEPROM; +	wrbuf[1] = off & 0xff; +	wrbuf[2] = off >> 8; +	w1_write_block(sl->master, wrbuf, 3); +	w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); + +	/* cache the block if the CRC is valid */ +	if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) +		data->validcrc |= (1 << block); + +	return 0; +} + +static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data) +{ +	u8 wrbuf[3]; + +	/* read directly from the EEPROM */ +	if (w1_reset_select_slave(sl)) +		return -EIO; + +	wrbuf[0] = W1_F1C_READ_EEPROM; +	wrbuf[1] = addr & 0xff; +	wrbuf[2] = addr >> 8; + +	w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); +	return w1_read_block(sl->master, data, len); +} + +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, +			   struct bin_attribute *bin_attr, char *buf, +			   loff_t off, size_t count) +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	struct w1_f1C_data *data = sl->family_data; +	int i, min_page, max_page; + +	count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); +	if (count == 0) +		return 0; + +	mutex_lock(&sl->master->mutex); + +	if (w1_enable_crccheck) { +		min_page = (off >> W1_PAGE_BITS); +		max_page = (off + count - 1) >> W1_PAGE_BITS; +		for (i = min_page; i <= max_page; i++) { +			if (w1_f1C_refresh_block(sl, data, i)) { +				count = -EIO; +				goto out_up; +			} +		} +		memcpy(buf, &data->memory[off], count); +	} else { +		count = w1_f1C_read(sl, off, count, buf); +	} + +out_up: +	mutex_unlock(&sl->master->mutex); + +	return count; +} + +/** + * Writes to the scratchpad and reads it back for verification. + * Then copies the scratchpad to EEPROM. + * The data must be on one page. + * The master must be locked. + * + * @param sl	The slave structure + * @param addr	Address for the write + * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) + * @param data	The data to write + * @return	0=Success -1=failure + */ +static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data) +{ +	u8 wrbuf[4]; +	u8 rdbuf[W1_PAGE_SIZE + 3]; +	u8 es = (addr + len - 1) & 0x1f; +	unsigned int tm = 10; +	int i; +	struct w1_f1C_data *f1C = sl->family_data; + +	/* Write the data to the scratchpad */ +	if (w1_reset_select_slave(sl)) +		return -1; + +	wrbuf[0] = W1_F1C_WRITE_SCRATCH; +	wrbuf[1] = addr & 0xff; +	wrbuf[2] = addr >> 8; + +	w1_write_block(sl->master, wrbuf, 3); +	w1_write_block(sl->master, data, len); + +	/* Read the scratchpad and verify */ +	if (w1_reset_select_slave(sl)) +		return -1; + +	w1_write_8(sl->master, W1_F1C_READ_SCRATCH); +	w1_read_block(sl->master, rdbuf, len + 3); + +	/* Compare what was read against the data written */ +	if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || +	    (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) +		return -1; + +	/* Copy the scratchpad to EEPROM */ +	if (w1_reset_select_slave(sl)) +		return -1; + +	wrbuf[0] = W1_F1C_COPY_SCRATCH; +	wrbuf[3] = es; + +	for (i = 0; i < sizeof(wrbuf); ++i) { +		/* issue 10ms strong pullup (or delay) on the last byte +		   for writing the data from the scratchpad to EEPROM */ +		if (w1_strong_pullup && i == sizeof(wrbuf)-1) +			w1_next_pullup(sl->master, tm); + +		w1_write_8(sl->master, wrbuf[i]); +	} + +	if (!w1_strong_pullup) +		msleep(tm); + +	if (w1_enable_crccheck) { +		/* invalidate cached data */ +		f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); +	} + +	/* Reset the bus to wake up the EEPROM (this may not be needed) */ +	w1_reset_bus(sl->master); + +	return 0; +} + +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, +			    struct bin_attribute *bin_attr, char *buf, +			    loff_t off, size_t count) + +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	int addr, len, idx; + +	count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); +	if (count == 0) +		return 0; + +	if (w1_enable_crccheck) { +		/* can only write full blocks in cached mode */ +		if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { +			dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n", +				(int)off, count); +			return -EINVAL; +		} + +		/* make sure the block CRCs are valid */ +		for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { +			if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) +				!= CRC16_VALID) { +				dev_err(&sl->dev, "bad CRC at offset %d\n", +					(int)off); +				return -EINVAL; +			} +		} +	} + +	mutex_lock(&sl->master->mutex); + +	/* Can only write data to one page at a time */ +	idx = 0; +	while (idx < count) { +		addr = off + idx; +		len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK); +		if (len > (count - idx)) +			len = count - idx; + +		if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) { +			count = -EIO; +			goto out_up; +		} +		idx += len; +	} + +out_up: +	mutex_unlock(&sl->master->mutex); + +	return count; +} + +static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static ssize_t pio_read(struct file *filp, struct kobject *kobj, +			struct bin_attribute *bin_attr, char *buf, loff_t off, +			size_t count) + +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	int ret; + +	/* check arguments */ +	if (off != 0 || count != 1 || buf == NULL) +		return -EINVAL; + +	mutex_lock(&sl->master->mutex); +	ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf); +	mutex_unlock(&sl->master->mutex); + +	return ret; +} + +static ssize_t pio_write(struct file *filp, struct kobject *kobj, +			 struct bin_attribute *bin_attr, char *buf, loff_t off, +			 size_t count) + +{ +	struct w1_slave *sl = kobj_to_w1_slave(kobj); +	u8 wrbuf[3]; +	u8 ack; + +	/* check arguments */ +	if (off != 0 || count != 1 || buf == NULL) +		return -EINVAL; + +	mutex_lock(&sl->master->mutex); + +	/* Write the PIO data */ +	if (w1_reset_select_slave(sl)) { +		mutex_unlock(&sl->master->mutex); +		return -1; +	} + +	/* set bit 7..2 to value '1' */ +	*buf = *buf | 0xFC; + +	wrbuf[0] = W1_F1C_ACCESS_WRITE; +	wrbuf[1] = *buf; +	wrbuf[2] = ~(*buf); +	w1_write_block(sl->master, wrbuf, 3); + +	w1_read_block(sl->master, &ack, sizeof(ack)); + +	mutex_unlock(&sl->master->mutex); + +	/* check for acknowledgement */ +	if (ack != 0xAA) +		return -EIO; + +	return count; +} + +static BIN_ATTR_RW(pio, 1); + +static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr, +			     char *buf) +{ +	if (put_user(w1_enable_crccheck + 0x30, buf)) +		return -EFAULT; + +	return sizeof(w1_enable_crccheck); +} + +static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr, +			      const char *buf, size_t count) +{ +	char val; + +	if (count != 1 || !buf) +		return -EINVAL; + +	if (get_user(val, buf)) +		return -EFAULT; + +	/* convert to decimal */ +	val = val - 0x30; +	if (val != 0 && val != 1) +		return -EINVAL; + +	/* set the new value */ +	w1_enable_crccheck = val; + +	return sizeof(w1_enable_crccheck); +} + +static DEVICE_ATTR_RW(crccheck); + +static struct attribute *w1_f1C_attrs[] = { +	&dev_attr_crccheck.attr, +	NULL, +}; + +static struct bin_attribute *w1_f1C_bin_attrs[] = { +	&bin_attr_eeprom, +	&bin_attr_pio, +	NULL, +}; + +static const struct attribute_group w1_f1C_group = { +	.attrs		= w1_f1C_attrs, +	.bin_attrs	= w1_f1C_bin_attrs, +}; + +static const struct attribute_group *w1_f1C_groups[] = { +	&w1_f1C_group, +	NULL, +}; + +static int w1_f1C_add_slave(struct w1_slave *sl) +{ +	struct w1_f1C_data *data = NULL; + +	if (w1_enable_crccheck) { +		data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL); +		if (!data) +			return -ENOMEM; +		sl->family_data = data; +	} + +	return 0; +} + +static void w1_f1C_remove_slave(struct w1_slave *sl) +{ +	kfree(sl->family_data); +	sl->family_data = NULL; +} + +static struct w1_family_ops w1_f1C_fops = { +	.add_slave      = w1_f1C_add_slave, +	.remove_slave   = w1_f1C_remove_slave, +	.groups		= w1_f1C_groups, +}; + +static struct w1_family w1_family_1C = { +	.fid = W1_FAMILY_DS28E04, +	.fops = &w1_f1C_fops, +}; + +static int __init w1_f1C_init(void) +{ +	return w1_register_family(&w1_family_1C); +} + +static void __exit w1_f1C_fini(void) +{ +	w1_unregister_family(&w1_family_1C); +} + +module_init(w1_f1C_init); +module_exit(w1_f1C_fini); diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c index cc8c02e9259..ed4c87506de 100644 --- a/drivers/w1/slaves/w1_smem.c +++ b/drivers/w1/slaves/w1_smem.c @@ -1,7 +1,7 @@  /*   *	w1_smem.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -32,8 +32,10 @@  #include "../w1_family.h"  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");  MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family."); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01)); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81));  static struct w1_family w1_smem_family_01 = {  	.fid = W1_FAMILY_SMEM_01, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 17726a05a0a..1f11a20a8ab 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -1,7 +1,7 @@  /*   *	w1_therm.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@  #include <linux/sched.h>  #include <linux/device.h>  #include <linux/types.h> +#include <linux/slab.h>  #include <linux/delay.h>  #include "../w1.h" @@ -34,41 +35,59 @@  #include "../w1_family.h"  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");  MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00));  /* Allow the strong pullup to be disabled, but default to enabled.   * If it was disabled a parasite powered device might not get the require   * current to do a temperature conversion.  If it is enabled parasite powered   * devices have a better chance of getting the current required. + * In case the parasite power-detection is not working (seems to be the case + * for some DS18S20) the strong pullup can also be forced, regardless of the + * power state of the devices. + * + * Summary of options: + * - strong_pullup = 0	Disable strong pullup completely + * - strong_pullup = 1	Enable automatic strong pullup detection + * - strong_pullup = 2	Force strong pullup   */  static int w1_strong_pullup = 1;  module_param_named(strong_pullup, w1_strong_pullup, int, 0); -static u8 bad_roms[][9] = { -				{0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87}, -				{} -			}; - -static ssize_t w1_therm_read(struct device *device, -	struct device_attribute *attr, char *buf); - -static struct device_attribute w1_therm_attr = -	__ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL); -  static int w1_therm_add_slave(struct w1_slave *sl)  { -	return device_create_file(&sl->dev, &w1_therm_attr); +	sl->family_data = kzalloc(9, GFP_KERNEL); +	if (!sl->family_data) +		return -ENOMEM; +	return 0;  }  static void w1_therm_remove_slave(struct w1_slave *sl)  { -	device_remove_file(&sl->dev, &w1_therm_attr); +	kfree(sl->family_data); +	sl->family_data = NULL;  } +static ssize_t w1_slave_show(struct device *device, +	struct device_attribute *attr, char *buf); + +static DEVICE_ATTR_RO(w1_slave); + +static struct attribute *w1_therm_attrs[] = { +	&dev_attr_w1_slave.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(w1_therm); +  static struct w1_family_ops w1_therm_fops = {  	.add_slave	= w1_therm_add_slave,  	.remove_slave	= w1_therm_remove_slave, +	.groups		= w1_therm_groups,  };  static struct w1_family w1_therm_family_DS18S20 = { @@ -86,6 +105,16 @@ static struct w1_family w1_therm_family_DS1822 = {  	.fops = &w1_therm_fops,  }; +static struct w1_family w1_therm_family_DS28EA00 = { +	.fid = W1_THERM_DS28EA00, +	.fops = &w1_therm_fops, +}; + +static struct w1_family w1_therm_family_DS1825 = { +	.fid = W1_THERM_DS1825, +	.fops = &w1_therm_fops, +}; +  struct w1_therm_family_converter  {  	u8			broken; @@ -111,6 +140,14 @@ static struct w1_therm_family_converter w1_therm_families[] = {  		.f		= &w1_therm_family_DS18B20,  		.convert 	= w1_DS18B20_convert_temp  	}, +	{ +		.f		= &w1_therm_family_DS28EA00, +		.convert	= w1_DS18B20_convert_temp +	}, +	{ +		.f		= &w1_therm_family_DS1825, +		.convert	= w1_DS18B20_convert_temp +	}  };  static inline int w1_DS18B20_convert_temp(u8 rom[9]) @@ -150,44 +187,62 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid)  	return 0;  } -static int w1_therm_check_rom(u8 rom[9]) -{ -	int i; - -	for (i=0; i<sizeof(bad_roms)/9; ++i) -		if (!memcmp(bad_roms[i], rom, 9)) -			return 1; - -	return 0; -} -static ssize_t w1_therm_read(struct device *device, +static ssize_t w1_slave_show(struct device *device,  	struct device_attribute *attr, char *buf)  {  	struct w1_slave *sl = dev_to_w1_slave(device);  	struct w1_master *dev = sl->master; -	u8 rom[9], crc, verdict; +	u8 rom[9], crc, verdict, external_power;  	int i, max_trying = 10;  	ssize_t c = PAGE_SIZE; -	mutex_lock(&dev->mutex); +	i = mutex_lock_interruptible(&dev->bus_mutex); +	if (i != 0) +		return i;  	memset(rom, 0, sizeof(rom)); -	verdict = 0; -	crc = 0; -  	while (max_trying--) { + +		verdict = 0; +		crc = 0; +  		if (!w1_reset_select_slave(sl)) {  			int count = 0;  			unsigned int tm = 750; +			unsigned long sleep_rem; + +			w1_write_8(dev, W1_READ_PSUPPLY); +			external_power = w1_read_8(dev); + +			if (w1_reset_select_slave(sl)) +				continue;  			/* 750ms strong pullup (or delay) after the convert */ -			if (w1_strong_pullup) +			if (w1_strong_pullup == 2 || +					(!external_power && w1_strong_pullup))  				w1_next_pullup(dev, tm); +  			w1_write_8(dev, W1_CONVERT_TEMP); -			if (!w1_strong_pullup) -				msleep(tm); + +			if (external_power) { +				mutex_unlock(&dev->bus_mutex); + +				sleep_rem = msleep_interruptible(tm); +				if (sleep_rem != 0) +					return -EINTR; + +				i = mutex_lock_interruptible(&dev->bus_mutex); +				if (i != 0) +					return i; +			} else if (!w1_strong_pullup) { +				sleep_rem = msleep_interruptible(tm); +				if (sleep_rem != 0) { +					mutex_unlock(&dev->bus_mutex); +					return -EINTR; +				} +			}  			if (!w1_reset_select_slave(sl)) { @@ -205,7 +260,7 @@ static ssize_t w1_therm_read(struct device *device,  			}  		} -		if (!w1_therm_check_rom(rom)) +		if (verdict)  			break;  	} @@ -214,16 +269,17 @@ static ssize_t w1_therm_read(struct device *device,  	c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",  			   crc, (verdict) ? "YES" : "NO");  	if (verdict) -		memcpy(sl->rom, rom, sizeof(sl->rom)); +		memcpy(sl->family_data, rom, sizeof(rom));  	else -		dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n"); +		dev_warn(device, "Read failed CRC check\n");  	for (i = 0; i < 9; ++i) -		c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]); +		c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", +			      ((u8 *)sl->family_data)[i]);  	c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",  		w1_convert_temp(rom, sl->family->fid)); -	mutex_unlock(&dev->mutex); +	mutex_unlock(&dev->bus_mutex);  	return PAGE_SIZE - c;  } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index b7b5014ff71..5d734152054 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -1,7 +1,7 @@  /*   *	w1.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@  #include <linux/kthread.h>  #include <linux/freezer.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include "w1.h"  #include "w1_log.h" @@ -42,22 +42,33 @@  #include "w1_netlink.h"  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");  MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");  static int w1_timeout = 10; -int w1_max_slave_count = 10; +int w1_max_slave_count = 64;  int w1_max_slave_ttl = 10;  module_param_named(timeout, w1_timeout, int, 0); +MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches"); +/* A search stops when w1_max_slave_count devices have been found in that + * search.  The next search will start over and detect the same set of devices + * on a static 1-wire bus.  Memory is not allocated based on this number, just + * on the number of devices known to the kernel.  Having a high number does not + * consume additional resources.  As a special case, if there is only one + * device on the network and w1_max_slave_count is set to 1, the device id can + * be read directly skipping the normal slower search process. + */  module_param_named(max_slave_count, w1_max_slave_count, int, 0); +MODULE_PARM_DESC(max_slave_count, +	"maximum number of slaves detected in a search");  module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); +MODULE_PARM_DESC(slave_ttl, +	"Number of searches not seeing a slave before it will be removed");  DEFINE_MUTEX(w1_mlock);  LIST_HEAD(w1_masters); -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); -  static int w1_master_match(struct device *dev, struct device_driver *drv)  {  	return 1; @@ -81,29 +92,21 @@ static void w1_slave_release(struct device *dev)  {  	struct w1_slave *sl = dev_to_w1_slave(dev); -	dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); - -	while (atomic_read(&sl->refcnt)) { -		dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n", -				sl->name, atomic_read(&sl->refcnt)); -		if (msleep_interruptible(1000)) -			flush_signals(current); -	} +	dev_dbg(dev, "%s: Releasing %s [%p]\n", __func__, sl->name, sl);  	w1_family_put(sl->family);  	sl->master->slave_count--; - -	complete(&sl->released);  } -static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct w1_slave *sl = dev_to_w1_slave(dev);  	return sprintf(buf, "%s\n", sl->name);  } +static DEVICE_ATTR_RO(name); -static ssize_t w1_slave_read_id(struct device *dev, +static ssize_t id_show(struct device *dev,  	struct device_attribute *attr, char *buf)  {  	struct w1_slave *sl = dev_to_w1_slave(dev); @@ -112,17 +115,20 @@ static ssize_t w1_slave_read_id(struct device *dev,  	memcpy(buf, (u8 *)&sl->reg_num, count);  	return count;  } +static DEVICE_ATTR_RO(id); -static struct device_attribute w1_slave_attr_name = -	__ATTR(name, S_IRUGO, w1_slave_read_name, NULL); -static struct device_attribute w1_slave_attr_id = -	__ATTR(id, S_IRUGO, w1_slave_read_id, NULL); +static struct attribute *w1_slave_attrs[] = { +	&dev_attr_name.attr, +	&dev_attr_id.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(w1_slave);  /* Default family */ -static ssize_t w1_default_write(struct file *filp, struct kobject *kobj, -				struct bin_attribute *bin_attr, -				char *buf, loff_t off, size_t count) +static ssize_t rw_write(struct file *filp, struct kobject *kobj, +			struct bin_attribute *bin_attr, char *buf, loff_t off, +			size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -139,9 +145,9 @@ out_up:  	return count;  } -static ssize_t w1_default_read(struct file *filp, struct kobject *kobj, -			       struct bin_attribute *bin_attr, -			       char *buf, loff_t off, size_t count) +static ssize_t rw_read(struct file *filp, struct kobject *kobj, +		       struct bin_attribute *bin_attr, char *buf, loff_t off, +		       size_t count)  {  	struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -151,29 +157,24 @@ static ssize_t w1_default_read(struct file *filp, struct kobject *kobj,  	return count;  } -static struct bin_attribute w1_default_attr = { -      .attr = { -              .name = "rw", -              .mode = S_IRUGO | S_IWUSR, -      }, -      .size = PAGE_SIZE, -      .read = w1_default_read, -      .write = w1_default_write, +static BIN_ATTR_RW(rw, PAGE_SIZE); + +static struct bin_attribute *w1_slave_bin_attrs[] = { +	&bin_attr_rw, +	NULL,  }; -static int w1_default_add_slave(struct w1_slave *sl) -{ -	return sysfs_create_bin_file(&sl->dev.kobj, &w1_default_attr); -} +static const struct attribute_group w1_slave_default_group = { +	.bin_attrs = w1_slave_bin_attrs, +}; -static void w1_default_remove_slave(struct w1_slave *sl) -{ -	sysfs_remove_bin_file(&sl->dev.kobj, &w1_default_attr); -} +static const struct attribute_group *w1_slave_default_groups[] = { +	&w1_slave_default_group, +	NULL, +};  static struct w1_family_ops w1_default_fops = { -	.add_slave	= w1_default_add_slave, -	.remove_slave	= w1_default_remove_slave, +	.groups		= w1_slave_default_groups,  };  static struct w1_family w1_default_family = { @@ -235,14 +236,18 @@ static ssize_t w1_master_attribute_store_search(struct device * dev,  {  	long tmp;  	struct w1_master *md = dev_to_w1_master(dev); +	int ret; -	if (strict_strtol(buf, 0, &tmp) == -EINVAL) -		return -EINVAL; +	ret = kstrtol(buf, 0, &tmp); +	if (ret) +		return ret;  	mutex_lock(&md->mutex);  	md->search_count = tmp;  	mutex_unlock(&md->mutex); -	wake_up_process(md->thread); +	/* Only wake if it is going to be searching. */ +	if (tmp) +		wake_up_process(md->thread);  	return count;  } @@ -267,14 +272,15 @@ static ssize_t w1_master_attribute_store_pullup(struct device *dev,  {  	long tmp;  	struct w1_master *md = dev_to_w1_master(dev); +	int ret; -	if (strict_strtol(buf, 0, &tmp) == -EINVAL) -		return -EINVAL; +	ret = kstrtol(buf, 0, &tmp); +	if (ret) +		return ret;  	mutex_lock(&md->mutex);  	md->enable_pullup = tmp;  	mutex_unlock(&md->mutex); -	wake_up_process(md->thread);  	return count;  } @@ -311,6 +317,24 @@ static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct devic  	return count;  } +static ssize_t w1_master_attribute_store_max_slave_count(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	int tmp; +	struct w1_master *md = dev_to_w1_master(dev); + +	if (kstrtoint(buf, 0, &tmp) == -EINVAL || tmp < 1) +		return -EINVAL; + +	mutex_lock(&md->mutex); +	md->max_slave_count = tmp; +	/* allow each time the max_slave_count is updated */ +	clear_bit(W1_WARN_MAX_COUNT, &md->flags); +	mutex_unlock(&md->mutex); + +	return count; +} +  static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct w1_master *md = dev_to_w1_master(dev); @@ -349,23 +373,20 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev,  {  	struct w1_master *md = dev_to_w1_master(dev);  	int c = PAGE_SIZE; +	struct list_head *ent, *n; +	struct w1_slave *sl = NULL; -	mutex_lock(&md->mutex); - -	if (md->slave_count == 0) -		c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); -	else { -		struct list_head *ent, *n; -		struct w1_slave *sl; +	mutex_lock(&md->list_mutex); -		list_for_each_safe(ent, n, &md->slist) { -			sl = list_entry(ent, struct w1_slave, w1_slave_entry); +	list_for_each_safe(ent, n, &md->slist) { +		sl = list_entry(ent, struct w1_slave, w1_slave_entry); -			c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); -		} +		c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name);  	} +	if (!sl) +		c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); -	mutex_unlock(&md->mutex); +	mutex_unlock(&md->list_mutex);  	return PAGE_SIZE - c;  } @@ -419,19 +440,22 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,  }  /* Searches the slaves in the w1_master and returns a pointer or NULL. - * Note: must hold the mutex + * Note: must not hold list_mutex   */ -static struct w1_slave *w1_slave_search_device(struct w1_master *dev, +struct w1_slave *w1_slave_search_device(struct w1_master *dev,  	struct w1_reg_num *rn)  {  	struct w1_slave *sl; +	mutex_lock(&dev->list_mutex);  	list_for_each_entry(sl, &dev->slist, w1_slave_entry) {  		if (sl->reg_num.family == rn->family &&  				sl->reg_num.id == rn->id &&  				sl->reg_num.crc == rn->crc) { +			mutex_unlock(&dev->list_mutex);  			return sl;  		}  	} +	mutex_unlock(&dev->list_mutex);  	return NULL;  } @@ -488,7 +512,10 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev,  	mutex_lock(&md->mutex);  	sl = w1_slave_search_device(md, &rn);  	if (sl) { -		w1_slave_detach(sl); +		result = w1_slave_detach(sl); +		/* refcnt 0 means it was detached in the call */ +		if (result == 0) +			result = count;  	} else {  		dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family,  			(unsigned long long)rn.id); @@ -513,7 +540,7 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev,  static W1_MASTER_ATTR_RO(name, S_IRUGO);  static W1_MASTER_ATTR_RO(slaves, S_IRUGO);  static W1_MASTER_ATTR_RO(slave_count, S_IRUGO); -static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO); +static W1_MASTER_ATTR_RW(max_slave_count, S_IRUGO | S_IWUSR | S_IWGRP);  static W1_MASTER_ATTR_RO(attempts, S_IRUGO);  static W1_MASTER_ATTR_RO(timeout, S_IRUGO);  static W1_MASTER_ATTR_RO(pointer, S_IRUGO); @@ -551,13 +578,12 @@ void w1_destroy_master_attributes(struct w1_master *master)  	sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group);  } -#ifdef CONFIG_HOTPLUG  static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)  {  	struct w1_master *md = NULL;  	struct w1_slave *sl = NULL;  	char *event_owner, *name; -	int err; +	int err = 0;  	if (dev->driver == &w1_master_driver) {  		md = container_of(dev, struct w1_master, dev); @@ -576,25 +602,60 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)  			event_owner, name, dev_name(dev));  	if (dev->driver != &w1_slave_driver || !sl) -		return 0; +		goto end;  	err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);  	if (err) -		return err; +		goto end;  	err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",  			     (unsigned long long)sl->reg_num.id); -	if (err) -		return err; +end: +	return err; +} -	return 0; -}; -#else -static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) +static int w1_family_notify(unsigned long action, struct w1_slave *sl)  { +	struct w1_family_ops *fops; +	int err; + +	fops = sl->family->fops; + +	if (!fops) +		return 0; + +	switch (action) { +	case BUS_NOTIFY_ADD_DEVICE: +		/* if the family driver needs to initialize something... */ +		if (fops->add_slave) { +			err = fops->add_slave(sl); +			if (err < 0) { +				dev_err(&sl->dev, +					"add_slave() call failed. err=%d\n", +					err); +				return err; +			} +		} +		if (fops->groups) { +			err = sysfs_create_groups(&sl->dev.kobj, fops->groups); +			if (err) { +				dev_err(&sl->dev, +					"sysfs group creation failed. err=%d\n", +					err); +				return err; +			} +		} + +		break; +	case BUS_NOTIFY_DEL_DEVICE: +		if (fops->remove_slave) +			sl->family->fops->remove_slave(sl); +		if (fops->groups) +			sysfs_remove_groups(&sl->dev.kobj, fops->groups); +		break; +	}  	return 0;  } -#endif  static int __w1_attach_slave_device(struct w1_slave *sl)  { @@ -604,6 +665,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)  	sl->dev.driver = &w1_slave_driver;  	sl->dev.bus = &w1_bus_type;  	sl->dev.release = &w1_slave_release; +	sl->dev.groups = w1_slave_groups;  	dev_set_name(&sl->dev, "%02x-%012llx",  		 (unsigned int) sl->reg_num.family, @@ -616,6 +678,9 @@ static int __w1_attach_slave_device(struct w1_slave *sl)  	dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__,  		dev_name(&sl->dev), sl); +	/* suppress for w1_family_notify before sending KOBJ_ADD */ +	dev_set_uevent_suppress(&sl->dev, true); +  	err = device_register(&sl->dev);  	if (err < 0) {  		dev_err(&sl->dev, @@ -623,48 +688,19 @@ static int __w1_attach_slave_device(struct w1_slave *sl)  			dev_name(&sl->dev), err);  		return err;  	} +	w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl); -	/* Create "name" entry */ -	err = device_create_file(&sl->dev, &w1_slave_attr_name); -	if (err < 0) { -		dev_err(&sl->dev, -			"sysfs file creation for [%s] failed. err=%d\n", -			dev_name(&sl->dev), err); -		goto out_unreg; -	} - -	/* Create "id" entry */ -	err = device_create_file(&sl->dev, &w1_slave_attr_id); -	if (err < 0) { -		dev_err(&sl->dev, -			"sysfs file creation for [%s] failed. err=%d\n", -			dev_name(&sl->dev), err); -		goto out_rem1; -	} - -	/* if the family driver needs to initialize something... */ -	if (sl->family->fops && sl->family->fops->add_slave && -	    ((err = sl->family->fops->add_slave(sl)) < 0)) { -		dev_err(&sl->dev, -			"sysfs file creation for [%s] failed. err=%d\n", -			dev_name(&sl->dev), err); -		goto out_rem2; -	} +	dev_set_uevent_suppress(&sl->dev, false); +	kobject_uevent(&sl->dev.kobj, KOBJ_ADD); +	mutex_lock(&sl->master->list_mutex);  	list_add_tail(&sl->w1_slave_entry, &sl->master->slist); +	mutex_unlock(&sl->master->list_mutex);  	return 0; - -out_rem2: -	device_remove_file(&sl->dev, &w1_slave_attr_id); -out_rem1: -	device_remove_file(&sl->dev, &w1_slave_attr_name); -out_unreg: -	device_unregister(&sl->dev); -	return err;  } -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)  {  	struct w1_slave *sl;  	struct w1_family *f; @@ -682,12 +718,17 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)  	sl->owner = THIS_MODULE;  	sl->master = dev; -	set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); +	set_bit(W1_SLAVE_ACTIVE, &sl->flags);  	memset(&msg, 0, sizeof(msg));  	memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); -	atomic_set(&sl->refcnt, 0); -	init_completion(&sl->released); +	atomic_set(&sl->refcnt, 1); +	atomic_inc(&sl->master->refcnt); + +	/* slave modules need to be loaded in a context with unlocked mutex */ +	mutex_unlock(&dev->mutex); +	request_module("w1-family-0x%0x", rn->family); +	mutex_lock(&dev->mutex);  	spin_lock(&w1_flock);  	f = w1_family_registered(rn->family); @@ -722,28 +763,49 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)  	return 0;  } -void w1_slave_detach(struct w1_slave *sl) +int w1_unref_slave(struct w1_slave *sl)  { -	struct w1_netlink_msg msg; - -	dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl); - -	list_del(&sl->w1_slave_entry); - -	if (sl->family->fops && sl->family->fops->remove_slave) -		sl->family->fops->remove_slave(sl); - -	memset(&msg, 0, sizeof(msg)); -	memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); -	msg.type = W1_SLAVE_REMOVE; -	w1_netlink_send(sl->master, &msg); - -	device_remove_file(&sl->dev, &w1_slave_attr_id); -	device_remove_file(&sl->dev, &w1_slave_attr_name); -	device_unregister(&sl->dev); +	struct w1_master *dev = sl->master; +	int refcnt; +	mutex_lock(&dev->list_mutex); +	refcnt = atomic_sub_return(1, &sl->refcnt); +	if (refcnt == 0) { +		struct w1_netlink_msg msg; + +		dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, +			sl->name, sl); + +		list_del(&sl->w1_slave_entry); + +		memset(&msg, 0, sizeof(msg)); +		memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); +		msg.type = W1_SLAVE_REMOVE; +		w1_netlink_send(sl->master, &msg); + +		w1_family_notify(BUS_NOTIFY_DEL_DEVICE, sl); +		device_unregister(&sl->dev); +		#ifdef DEBUG +		memset(sl, 0, sizeof(*sl)); +		#endif +		kfree(sl); +	} +	atomic_dec(&dev->refcnt); +	mutex_unlock(&dev->list_mutex); +	return refcnt; +} -	wait_for_completion(&sl->released); -	kfree(sl); +int w1_slave_detach(struct w1_slave *sl) +{ +	/* Only detach a slave once as it decreases the refcnt each time. */ +	int destroy_now; +	mutex_lock(&sl->master->list_mutex); +	destroy_now = !test_bit(W1_SLAVE_DETACH, &sl->flags); +	set_bit(W1_SLAVE_DETACH, &sl->flags); +	mutex_unlock(&sl->master->list_mutex); + +	if (destroy_now) +		destroy_now = !w1_unref_slave(sl); +	return destroy_now ? 0 : -EBUSY;  }  struct w1_master *w1_search_master_id(u32 id) @@ -772,7 +834,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id)  	mutex_lock(&w1_mlock);  	list_for_each_entry(dev, &w1_masters, w1_master_entry) { -		mutex_lock(&dev->mutex); +		mutex_lock(&dev->list_mutex);  		list_for_each_entry(sl, &dev->slist, w1_slave_entry) {  			if (sl->reg_num.family == id->family &&  					sl->reg_num.id == id->id && @@ -783,7 +845,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id)  				break;  			}  		} -		mutex_unlock(&dev->mutex); +		mutex_unlock(&dev->list_mutex);  		if (found)  			break; @@ -803,6 +865,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)  		dev_dbg(&dev->dev, "Reconnecting slaves in device %s "  			"for family %02x.\n", dev->name, f->fid);  		mutex_lock(&dev->mutex); +		mutex_lock(&dev->list_mutex);  		list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {  			/* If it is a new family, slaves with the default  			 * family driver and are that family will be @@ -814,20 +877,25 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)  				(!attach && sl->family->fid == f->fid)) {  				struct w1_reg_num rn; +				mutex_unlock(&dev->list_mutex);  				memcpy(&rn, &sl->reg_num, sizeof(rn)); -				w1_slave_detach(sl); - -				w1_attach_slave_device(dev, &rn); +				/* If it was already in use let the automatic +				 * scan pick it up again later. +				 */ +				if (!w1_slave_detach(sl)) +					w1_attach_slave_device(dev, &rn); +				mutex_lock(&dev->list_mutex);  			}  		}  		dev_dbg(&dev->dev, "Reconnecting slaves in device %s "  			"has been finished.\n", dev->name); +		mutex_unlock(&dev->list_mutex);  		mutex_unlock(&dev->mutex);  	}  	mutex_unlock(&w1_mlock);  } -static void w1_slave_found(struct w1_master *dev, u64 rn) +void w1_slave_found(struct w1_master *dev, u64 rn)  {  	struct w1_slave *sl;  	struct w1_reg_num *tmp; @@ -839,7 +907,7 @@ static void w1_slave_found(struct w1_master *dev, u64 rn)  	sl = w1_slave_search_device(dev, tmp);  	if (sl) { -		set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); +		set_bit(W1_SLAVE_ACTIVE, &sl->flags);  	} else {  		if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7))  			w1_attach_slave_device(dev, tmp); @@ -849,7 +917,12 @@ static void w1_slave_found(struct w1_master *dev, u64 rn)  }  /** - * Performs a ROM Search & registers any devices found. + * w1_search() - Performs a ROM Search & registers any devices found. + * @dev: The master device to search + * @search_type: W1_SEARCH to search all devices, or W1_ALARM_SEARCH + * to return only devices in the alarmed state + * @cb: Function to call when a device is found + *   * The 1-wire search is a simple binary tree search.   * For each bit of the address, we read two bits and write one bit.   * The bit written will put to sleep all devies that don't match that bit. @@ -859,8 +932,6 @@ static void w1_slave_found(struct w1_master *dev, u64 rn)   *   * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com   * - * @dev        The master device to search - * @cb         Function to call when a device is found   */  void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb)  { @@ -871,7 +942,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb  	u8  triplet_ret = 0;  	search_bit = 0; -	rn = last_rn = 0; +	rn = dev->search_id; +	last_rn = 0;  	last_device = 0;  	last_zero = -1; @@ -887,11 +959,26 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb  		 *  		 * Return 0 - device(s) present, 1 - no devices present.  		 */ +		mutex_lock(&dev->bus_mutex);  		if (w1_reset_bus(dev)) { +			mutex_unlock(&dev->bus_mutex);  			dev_dbg(&dev->dev, "No devices present on the wire.\n");  			break;  		} +		/* Do fast search on single slave bus */ +		if (dev->max_slave_count == 1) { +			int rv; +			w1_write_8(dev, W1_READ_ROM); +			rv = w1_read_block(dev, (u8 *)&rn, 8); +			mutex_unlock(&dev->bus_mutex); + +			if (rv == 8 && rn) +				cb(dev, rn); + +			break; +		} +  		/* Start the search */  		w1_write_8(dev, search_type);  		for (i = 0; i < 64; ++i) { @@ -903,7 +990,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb  			else  				search_bit = ((last_rn >> i) & 0x1); -			/** Read two bits and write one bit */ +			/* Read two bits and write one bit */  			triplet_ret = w1_triplet(dev, search_bit);  			/* quit if no device responded */ @@ -918,41 +1005,103 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb  			tmp64 = (triplet_ret >> 2);  			rn |= (tmp64 << i); -			if (kthread_should_stop()) { +			if (test_bit(W1_ABORT_SEARCH, &dev->flags)) { +				mutex_unlock(&dev->bus_mutex);  				dev_dbg(&dev->dev, "Abort w1_search\n");  				return;  			}  		} +		mutex_unlock(&dev->bus_mutex);  		if ( (triplet_ret & 0x03) != 0x03 ) { -			if ( (desc_bit == last_zero) || (last_zero < 0)) +			if ((desc_bit == last_zero) || (last_zero < 0)) {  				last_device = 1; +				dev->search_id = 0; +			} else { +				dev->search_id = rn; +			}  			desc_bit = last_zero;  			cb(dev, rn);  		} + +		if (!last_device && slave_count == dev->max_slave_count && +			!test_bit(W1_WARN_MAX_COUNT, &dev->flags)) { +			/* Only max_slave_count will be scanned in a search, +			 * but it will start where it left off next search +			 * until all ids are identified and then it will start +			 * over.  A continued search will report the previous +			 * last id as the first id (provided it is still on the +			 * bus). +			 */ +			dev_info(&dev->dev, "%s: max_slave_count %d reached, " +				"will continue next search.\n", __func__, +				dev->max_slave_count); +			set_bit(W1_WARN_MAX_COUNT, &dev->flags); +		}  	}  } -void w1_search_process(struct w1_master *dev, u8 search_type) +void w1_search_process_cb(struct w1_master *dev, u8 search_type, +	w1_slave_found_callback cb)  {  	struct w1_slave *sl, *sln; +	mutex_lock(&dev->list_mutex);  	list_for_each_entry(sl, &dev->slist, w1_slave_entry) -		clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); +		clear_bit(W1_SLAVE_ACTIVE, &sl->flags); +	mutex_unlock(&dev->list_mutex); -	w1_search_devices(dev, search_type, w1_slave_found); +	w1_search_devices(dev, search_type, cb); +	mutex_lock(&dev->list_mutex);  	list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { -		if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) +		if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) { +			mutex_unlock(&dev->list_mutex);  			w1_slave_detach(sl); -		else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags)) +			mutex_lock(&dev->list_mutex); +		} +		else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags))  			sl->ttl = dev->slave_ttl;  	} +	mutex_unlock(&dev->list_mutex);  	if (dev->search_count > 0)  		dev->search_count--;  } +static void w1_search_process(struct w1_master *dev, u8 search_type) +{ +	w1_search_process_cb(dev, search_type, w1_slave_found); +} + +/** + * w1_process_callbacks() - execute each dev->async_list callback entry + * @dev: w1_master device + * + * The w1 master list_mutex must be held. + * + * Return: 1 if there were commands to executed 0 otherwise + */ +int w1_process_callbacks(struct w1_master *dev) +{ +	int ret = 0; +	struct w1_async_cmd *async_cmd, *async_n; + +	/* The list can be added to in another thread, loop until it is empty */ +	while (!list_empty(&dev->async_list)) { +		list_for_each_entry_safe(async_cmd, async_n, &dev->async_list, +			async_entry) { +			/* drop the lock, if it is a search it can take a long +			 * time */ +			mutex_unlock(&dev->list_mutex); +			async_cmd->cb(dev, async_cmd); +			ret = 1; +			mutex_lock(&dev->list_mutex); +		} +	} +	return ret; +} +  int w1_process(void *data)  {  	struct w1_master *dev = (struct w1_master *) data; @@ -960,23 +1109,46 @@ int w1_process(void *data)  	 * time can be calculated in jiffies once.  	 */  	const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); +	/* remainder if it woke up early */ +	unsigned long jremain = 0; -	while (!kthread_should_stop()) { -		if (dev->search_count) { +	for (;;) { + +		if (!jremain && dev->search_count) {  			mutex_lock(&dev->mutex);  			w1_search_process(dev, W1_SEARCH);  			mutex_unlock(&dev->mutex);  		} +		mutex_lock(&dev->list_mutex); +		/* Note, w1_process_callback drops the lock while processing, +		 * but locks it again before returning. +		 */ +		if (!w1_process_callbacks(dev) && jremain) { +			/* a wake up is either to stop the thread, process +			 * callbacks, or search, it isn't process callbacks, so +			 * schedule a search. +			 */ +			jremain = 1; +		} +  		try_to_freeze();  		__set_current_state(TASK_INTERRUPTIBLE); +		/* hold list_mutex until after interruptible to prevent loosing +		 * the wakeup signal when async_cmd is added. +		 */ +		mutex_unlock(&dev->list_mutex); +  		if (kthread_should_stop())  			break;  		/* Only sleep when the search is active. */ -		if (dev->search_count) -			schedule_timeout(jtime); +		if (dev->search_count) { +			if (!jremain) +				jremain = jtime; +			jremain = schedule_timeout(jremain); +		}  		else  			schedule();  	} @@ -1011,7 +1183,7 @@ static int __init w1_init(void)  	retval = driver_register(&w1_slave_driver);  	if (retval) {  		printk(KERN_ERR -			"Failed to register master driver. err=%d.\n", +			"Failed to register slave driver. err=%d.\n",  			retval);  		goto err_out_master_unregister;  	} diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index d8a9709f344..56a49ba41d8 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -1,7 +1,7 @@  /*   *	w1.h   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,13 @@  #ifndef __W1_H  #define __W1_H +/** + * struct w1_reg_num - broken out slave device id + * + * @family: identifies the type of device + * @id: along with family is the unique device id + * @crc: checksum of the other bytes + */  struct w1_reg_num  {  #if defined(__LITTLE_ENDIAN_BITFIELD) @@ -55,9 +62,27 @@ struct w1_reg_num  #define W1_READ_ROM		0x33  #define W1_READ_PSUPPLY		0xB4  #define W1_MATCH_ROM		0x55 +#define W1_RESUME_CMD		0xA5  #define W1_SLAVE_ACTIVE		0 +#define W1_SLAVE_DETACH		1 +/** + * struct w1_slave - holds a single slave device on the bus + * + * @owner: Points to the one wire "wire" kernel module. + * @name: Device id is ascii. + * @w1_slave_entry: data for the linked list + * @reg_num: the slave id in binary + * @refcnt: reference count, delete when 0 + * @flags: bit flags for W1_SLAVE_ACTIVE W1_SLAVE_DETACH + * @ttl: decrement per search this slave isn't found, deatch at 0 + * @master: bus which this slave is on + * @family: module for device family type + * @family_data: pointer for use by the family module + * @dev: kernel device identifier + * + */  struct w1_slave  {  	struct module		*owner; @@ -65,120 +90,166 @@ struct w1_slave  	struct list_head	w1_slave_entry;  	struct w1_reg_num	reg_num;  	atomic_t		refcnt; -	u8			rom[9]; -	u32			flags;  	int			ttl; +	unsigned long		flags;  	struct w1_master	*master;  	struct w1_family	*family;  	void			*family_data;  	struct device		dev; -	struct completion	released;  };  typedef void (*w1_slave_found_callback)(struct w1_master *, u64);  /** + * struct w1_bus_master - operations available on a bus master + * + * @data: the first parameter in all the functions below + * + * @read_bit: Sample the line level @return the level read (0 or 1) + * + * @write_bit: Sets the line level + * + * @touch_bit: the lowest-level function for devices that really support the + * 1-wire protocol. + * touch_bit(0) = write-0 cycle + * touch_bit(1) = write-1 / read cycle + * @return the bit read (0 or 1) + * + * @read_byte: Reads a bytes. Same as 8 touch_bit(1) calls. + * @return the byte read + * + * @write_byte: Writes a byte. Same as 8 touch_bit(x) calls. + * + * @read_block: Same as a series of read_byte() calls + * @return the number of bytes read + * + * @write_block: Same as a series of write_byte() calls + * + * @triplet: Combines two reads and a smart write for ROM searches + * @return bit0=Id bit1=comp_id bit2=dir_taken + * + * @reset_bus: long write-0 with a read for the presence pulse detection + * @return -1=Error, 0=Device present, 1=No device present + * + * @set_pullup: Put out a strong pull-up pulse of the specified duration. + * @return -1=Error, 0=completed + * + * @search: Really nice hardware can handles the different types of ROM search + * w1_master* is passed to the slave found callback. + * u8 is search_type, W1_SEARCH or W1_ALARM_SEARCH + *   * Note: read_bit and write_bit are very low level functions and should only   * be used with hardware that doesn't really support 1-wire operations,   * like a parallel/serial port.   * Either define read_bit and write_bit OR define, at minimum, touch_bit and   * reset_bus. + *   */  struct w1_bus_master  { -	/** the first parameter in all the functions below */  	void		*data; -	/** -	 * Sample the line level -	 * @return the level read (0 or 1) -	 */  	u8		(*read_bit)(void *); -	/** Sets the line level */  	void		(*write_bit)(void *, u8); -	/** -	 * touch_bit is the lowest-level function for devices that really -	 * support the 1-wire protocol. -	 * touch_bit(0) = write-0 cycle -	 * touch_bit(1) = write-1 / read cycle -	 * @return the bit read (0 or 1) -	 */  	u8		(*touch_bit)(void *, u8); -	/** -	 * Reads a bytes. Same as 8 touch_bit(1) calls. -	 * @return the byte read -	 */  	u8		(*read_byte)(void *); -	/** -	 * Writes a byte. Same as 8 touch_bit(x) calls. -	 */  	void		(*write_byte)(void *, u8); -	/** -	 * Same as a series of read_byte() calls -	 * @return the number of bytes read -	 */  	u8		(*read_block)(void *, u8 *, int); -	/** Same as a series of write_byte() calls */  	void		(*write_block)(void *, const u8 *, int); -	/** -	 * Combines two reads and a smart write for ROM searches -	 * @return bit0=Id bit1=comp_id bit2=dir_taken -	 */  	u8		(*triplet)(void *, u8); -	/** -	 * long write-0 with a read for the presence pulse detection -	 * @return -1=Error, 0=Device present, 1=No device present -	 */  	u8		(*reset_bus)(void *); -	/** -	 * Put out a strong pull-up pulse of the specified duration. -	 * @return -1=Error, 0=completed -	 */  	u8		(*set_pullup)(void *, int); -	/** Really nice hardware can handles the different types of ROM search -	 *  w1_master* is passed to the slave found callback. -	 */  	void		(*search)(void *, struct w1_master *,  		u8, w1_slave_found_callback);  }; +/** + * enum w1_master_flags - bitfields used in w1_master.flags + * @W1_ABORT_SEARCH: abort searching early on shutdown + * @W1_WARN_MAX_COUNT: limit warning when the maximum count is reached + */ +enum w1_master_flags { +	W1_ABORT_SEARCH = 0, +	W1_WARN_MAX_COUNT = 1, +}; + +/** + * struct w1_master - one per bus master + * @w1_master_entry:	master linked list + * @owner:		module owner + * @name:		dynamically allocate bus name + * @list_mutex:		protect slist and async_list + * @slist:		linked list of slaves + * @async_list:		linked list of netlink commands to execute + * @max_slave_count:	maximum number of slaves to search for at a time + * @slave_count:	current number of slaves known + * @attempts:		number of searches ran + * @slave_ttl:		number of searches before a slave is timed out + * @initialized:	prevent init/removal race conditions + * @id:			w1 bus number + * @search_count:	number of automatic searches to run, -1 unlimited + * @search_id:		allows continuing a search + * @refcnt:		reference count + * @priv:		private data storage + * @enable_pullup:	allows a strong pullup + * @pullup_duration:	time for the next strong pullup + * @flags:		one of w1_master_flags + * @thread:		thread for bus search and netlink commands + * @mutex:		protect most of w1_master + * @bus_mutex:		pretect concurrent bus access + * @driver:		sysfs driver + * @dev:		sysfs device + * @bus_master:		io operations available + * @seq:		sequence number used for netlink broadcasts + */  struct w1_master  {  	struct list_head	w1_master_entry;  	struct module		*owner;  	unsigned char		name[W1_MAXNAMELEN]; +	/* list_mutex protects just slist and async_list so slaves can be +	 * searched for and async commands added while the master has +	 * w1_master.mutex locked and is operating on the bus. +	 * lock order w1_mlock, w1_master.mutex, w1_master.list_mutex +	 */ +	struct mutex		list_mutex;  	struct list_head	slist; +	struct list_head	async_list;  	int			max_slave_count, slave_count;  	unsigned long		attempts;  	int			slave_ttl;  	int			initialized;  	u32			id;  	int			search_count; +	/* id to start searching on, to continue a search or 0 to restart */ +	u64			search_id;  	atomic_t		refcnt;  	void			*priv; -	int			priv_size;  	/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */  	int			enable_pullup;  	/** 5V strong pullup duration in milliseconds, zero disabled. */  	int			pullup_duration; +	long			flags; +  	struct task_struct	*thread;  	struct mutex		mutex; +	struct mutex		bus_mutex;  	struct device_driver	*driver;  	struct device		dev; @@ -188,12 +259,34 @@ struct w1_master  	u32			seq;  }; +/** + * struct w1_async_cmd - execute callback from the w1_process kthread + * @async_entry: link entry + * @cb: callback function, must list_del and destroy this list before + * returning + * + * When inserted into the w1_master async_list, w1_process will execute + * the callback.  Embed this into the structure with the command details. + */ +struct w1_async_cmd { +	struct list_head	async_entry; +	void (*cb)(struct w1_master *dev, struct w1_async_cmd *async_cmd); +}; +  int w1_create_master_attributes(struct w1_master *);  void w1_destroy_master_attributes(struct w1_master *master);  void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);  void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +/* call w1_unref_slave to release the reference counts w1_search_slave added */  struct w1_slave *w1_search_slave(struct w1_reg_num *id); -void w1_search_process(struct w1_master *dev, u8 search_type); +/* decrements the reference on sl->master and sl, and cleans up if zero + * returns the reference count after it has been decremented */ +int w1_unref_slave(struct w1_slave *sl); +void w1_slave_found(struct w1_master *dev, u64 rn); +void w1_search_process_cb(struct w1_master *dev, u8 search_type, +	w1_slave_found_callback cb); +struct w1_slave *w1_slave_search_device(struct w1_master *dev, +	struct w1_reg_num *rn);  struct w1_master *w1_search_master_id(u32 id);  /* Disconnect and reconnect devices in the given family.  Used for finding @@ -202,7 +295,9 @@ struct w1_master *w1_search_master_id(u32 id);   * has just been registered, to 0 when it has been unregistered.   */  void w1_reconnect_slaves(struct w1_family *f, int attach); -void w1_slave_detach(struct w1_slave *sl); +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); +/* 0 success, otherwise EBUSY */ +int w1_slave_detach(struct w1_slave *sl);  u8 w1_triplet(struct w1_master *dev, int bdir);  void w1_write_8(struct w1_master *, u8); @@ -213,6 +308,7 @@ void w1_write_block(struct w1_master *, const u8 *, int);  void w1_touch_block(struct w1_master *, u8 *, int);  u8 w1_read_block(struct w1_master *, u8 *, int);  int w1_reset_select_slave(struct w1_slave *sl); +int w1_reset_resume_command(struct w1_master *);  void w1_next_pullup(struct w1_master *, int);  static inline struct w1_slave* dev_to_w1_slave(struct device *dev) @@ -237,6 +333,7 @@ extern int w1_max_slave_ttl;  extern struct list_head w1_masters;  extern struct mutex w1_mlock; +extern int w1_process_callbacks(struct w1_master *dev);  extern int w1_process(void *);  #endif /* __KERNEL__ */ diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index 4a099041f28..3651ec801f4 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -1,7 +1,7 @@  /*   *	w1_family.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@  #include <linux/list.h>  #include <linux/sched.h>	/* schedule_timeout() */  #include <linux/delay.h> +#include <linux/export.h>  #include "w1_family.h"  #include "w1.h" @@ -30,6 +31,10 @@  DEFINE_SPINLOCK(w1_flock);  static LIST_HEAD(w1_families); +/** + * w1_register_family() - register a device family driver + * @newf:	family to register + */  int w1_register_family(struct w1_family *newf)  {  	struct list_head *ent, *n; @@ -58,6 +63,10 @@ int w1_register_family(struct w1_family *newf)  	return ret;  } +/** + * w1_unregister_family() - unregister a device family driver + * @fent:	family to unregister + */  void w1_unregister_family(struct w1_family *fent)  {  	struct list_head *ent, *n; @@ -130,9 +139,9 @@ void w1_family_get(struct w1_family *f)  void __w1_family_get(struct w1_family *f)  { -	smp_mb__before_atomic_inc(); +	smp_mb__before_atomic();  	atomic_inc(&f->refcnt); -	smp_mb__after_atomic_inc(); +	smp_mb__after_atomic();  }  EXPORT_SYMBOL(w1_unregister_family); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 3ca1b9298f2..26ca1343055 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -1,7 +1,7 @@  /*   *	w1_family.h   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -24,28 +24,50 @@  #include <linux/types.h>  #include <linux/device.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #define W1_FAMILY_DEFAULT	0  #define W1_FAMILY_SMEM_01	0x01  #define W1_FAMILY_SMEM_81	0x81  #define W1_THERM_DS18S20 	0x10 +#define W1_FAMILY_DS28E04	0x1C +#define W1_COUNTER_DS2423	0x1D  #define W1_THERM_DS1822  	0x22  #define W1_EEPROM_DS2433  	0x23  #define W1_THERM_DS18B20 	0x28 +#define W1_FAMILY_DS2408	0x29  #define W1_EEPROM_DS2431	0x2D  #define W1_FAMILY_DS2760	0x30 +#define W1_FAMILY_DS2780	0x32 +#define W1_FAMILY_DS2413	0x3A +#define W1_THERM_DS1825		0x3B +#define W1_FAMILY_DS2781	0x3D +#define W1_THERM_DS28EA00	0x42  #define MAXNAMELEN		32  struct w1_slave; +/** + * struct w1_family_ops - operations for a family type + * @add_slave: add_slave + * @remove_slave: remove_slave + * @groups: sysfs group + */  struct w1_family_ops  {  	int  (* add_slave)(struct w1_slave *);  	void (* remove_slave)(struct w1_slave *); +	const struct attribute_group **groups;  }; +/** + * struct w1_family - reference counted family structure. + * @family_entry:	family linked list + * @fid:		8 bit family identifier + * @fops:		operations for this family + * @refcnt:		reference counter + */  struct w1_family  {  	struct list_head	family_entry; diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index b50be3f1073..728039d2efe 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -1,7 +1,7 @@  /*   *	w1_int.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,8 @@  #include <linux/delay.h>  #include <linux/kthread.h>  #include <linux/slab.h> +#include <linux/export.h> +#include <linux/moduleparam.h>  #include "w1.h"  #include "w1_log.h" @@ -73,11 +75,15 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,  	atomic_set(&dev->refcnt, 2);  	INIT_LIST_HEAD(&dev->slist); +	INIT_LIST_HEAD(&dev->async_list);  	mutex_init(&dev->mutex); +	mutex_init(&dev->bus_mutex); +	mutex_init(&dev->list_mutex);  	memcpy(&dev->dev, device, sizeof(struct device));  	dev_set_name(&dev->dev, "w1_bus_master%u", dev->id);  	snprintf(dev->name, sizeof(dev->name), "w1_bus_master%u", dev->id); +	dev->dev.init_name = dev->name;  	dev->driver = driver; @@ -99,6 +105,10 @@ static void w1_free_dev(struct w1_master *dev)  	device_unregister(&dev->dev);  } +/** + * w1_add_master_device() - registers a new master device + * @master:	master bus device to register + */  int w1_add_master_device(struct w1_bus_master *master)  {  	struct w1_master *dev, *entry; @@ -113,18 +123,6 @@ int w1_add_master_device(struct w1_bus_master *master)  		printk(KERN_ERR "w1_add_master_device: invalid function set\n");  		return(-EINVAL);          } -	/* While it would be electrically possible to make a device that -	 * generated a strong pullup in bit bang mode, only hardare that -	 * controls 1-wire time frames are even expected to support a strong -	 * pullup.  w1_io.c would need to support calling set_pullup before -	 * the last write_bit operation of a w1_write_8 which it currently -	 * doesn't. -	 */ -	if (!master->write_byte && !master->touch_bit && master->set_pullup) { -		printk(KERN_ERR "w1_add_master_device: set_pullup requires " -			"write_byte or touch_bit, disabling\n"); -		master->set_pullup = NULL; -	}  	/* Lock until the device is added (or not) to w1_masters. */  	mutex_lock(&w1_mlock); @@ -180,6 +178,7 @@ int w1_add_master_device(struct w1_bus_master *master)  #if 0 /* Thread cleanup code, not required currently. */  err_out_kill_thread: +	set_bit(W1_ABORT_SEARCH, &dev->flags);  	kthread_stop(dev->thread);  #endif  err_out_rm_attr: @@ -195,16 +194,22 @@ void __w1_remove_master_device(struct w1_master *dev)  	struct w1_netlink_msg msg;  	struct w1_slave *sl, *sln; -	kthread_stop(dev->thread); -  	mutex_lock(&w1_mlock);  	list_del(&dev->w1_master_entry);  	mutex_unlock(&w1_mlock); +	set_bit(W1_ABORT_SEARCH, &dev->flags); +	kthread_stop(dev->thread); +  	mutex_lock(&dev->mutex); -	list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) +	mutex_lock(&dev->list_mutex); +	list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { +		mutex_unlock(&dev->list_mutex);  		w1_slave_detach(sl); +		mutex_lock(&dev->list_mutex); +	}  	w1_destroy_master_attributes(dev); +	mutex_unlock(&dev->list_mutex);  	mutex_unlock(&dev->mutex);  	atomic_dec(&dev->refcnt); @@ -214,7 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev)  		if (msleep_interruptible(1000))  			flush_signals(current); +		mutex_lock(&dev->list_mutex); +		w1_process_callbacks(dev); +		mutex_unlock(&dev->list_mutex);  	} +	mutex_lock(&dev->list_mutex); +	w1_process_callbacks(dev); +	mutex_unlock(&dev->list_mutex);  	memset(&msg, 0, sizeof(msg));  	msg.id.mst.id = dev->id; @@ -224,6 +235,10 @@ void __w1_remove_master_device(struct w1_master *dev)  	w1_free_dev(dev);  } +/** + * w1_remove_master_device() - unregister a master device + * @bm:	master bus device to remove + */  void w1_remove_master_device(struct w1_bus_master *bm)  {  	struct w1_master *dev, *found = NULL; diff --git a/drivers/w1/w1_int.h b/drivers/w1/w1_int.h index 4274082d226..2ad7d4414be 100644 --- a/drivers/w1/w1_int.h +++ b/drivers/w1/w1_int.h @@ -1,7 +1,7 @@  /*   *	w1_int.h   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 3ebe9726a9e..282092421cc 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -1,7 +1,7 @@  /*   *	w1_io.c   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -31,6 +31,9 @@  static int w1_delay_parm = 1;  module_param_named(delay_coef, w1_delay_parm, int, 0); +static int w1_disable_irqs = 0; +module_param_named(disable_irqs, w1_disable_irqs, int, 0); +  static u8 w1_crc8_table[] = {  	0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,  	157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, @@ -59,7 +62,9 @@ static void w1_write_bit(struct w1_master *dev, int bit);  static u8 w1_read_bit(struct w1_master *dev);  /** - * Generates a write-0 or write-1 cycle and samples the level. + * w1_touch_bit() - Generates a write-0 or write-1 cycle and samples the level. + * @dev:	the master device + * @bit:	0 - write a 0, 1 - write a 0 read the level   */  static u8 w1_touch_bit(struct w1_master *dev, int bit)  { @@ -74,11 +79,18 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit)  }  /** - * Generates a write-0 or write-1 cycle. + * w1_write_bit() - Generates a write-0 or write-1 cycle. + * @dev:	the master device + * @bit:	bit to write + *   * Only call if dev->bus_master->touch_bit is NULL   */  static void w1_write_bit(struct w1_master *dev, int bit)  { +	unsigned long flags = 0; + +	if(w1_disable_irqs) local_irq_save(flags); +  	if (bit) {  		dev->bus_master->write_bit(dev->bus_master->data, 0);  		w1_delay(6); @@ -90,14 +102,17 @@ static void w1_write_bit(struct w1_master *dev, int bit)  		dev->bus_master->write_bit(dev->bus_master->data, 1);  		w1_delay(10);  	} + +	if(w1_disable_irqs) local_irq_restore(flags);  }  /** + * w1_pre_write() - pre-write operations + * @dev:	the master device + *   * Pre-write operation, currently only supporting strong pullups.   * Program the hardware for a strong pullup, if one has been requested and   * the hardware supports it. - * - * @param dev     the master device   */  static void w1_pre_write(struct w1_master *dev)  { @@ -109,11 +124,12 @@ static void w1_pre_write(struct w1_master *dev)  }  /** + * w1_post_write() - post-write options + * @dev:	the master device + *   * Post-write operation, currently only supporting strong pullups.   * If a strong pullup was requested, clear it if the hardware supports   * them, or execute the delay otherwise, in either case clear the request. - * - * @param dev     the master device   */  static void w1_post_write(struct w1_master *dev)  { @@ -127,10 +143,9 @@ static void w1_post_write(struct w1_master *dev)  }  /** - * Writes 8 bits. - * - * @param dev     the master device - * @param byte    the byte to write + * w1_write_8() - Writes 8 bits. + * @dev:	the master device + * @byte:	the byte to write   */  void w1_write_8(struct w1_master *dev, u8 byte)  { @@ -152,35 +167,43 @@ EXPORT_SYMBOL_GPL(w1_write_8);  /** - * Generates a write-1 cycle and samples the level. + * w1_read_bit() - Generates a write-1 cycle and samples the level. + * @dev:	the master device + *   * Only call if dev->bus_master->touch_bit is NULL   */  static u8 w1_read_bit(struct w1_master *dev)  {  	int result; +	unsigned long flags = 0; +	/* sample timing is critical here */ +	local_irq_save(flags);  	dev->bus_master->write_bit(dev->bus_master->data, 0);  	w1_delay(6);  	dev->bus_master->write_bit(dev->bus_master->data, 1);  	w1_delay(9);  	result = dev->bus_master->read_bit(dev->bus_master->data); +	local_irq_restore(flags); +  	w1_delay(55);  	return result & 0x1;  }  /** - * Does a triplet - used for searching ROM addresses. + * w1_triplet() - * Does a triplet - used for searching ROM addresses. + * @dev:	the master device + * @bdir:	the bit to write if both id_bit and comp_bit are 0 + *   * Return bits:   *  bit 0 = id_bit   *  bit 1 = comp_bit   *  bit 2 = dir_taken   * If both bits 0 & 1 are set, the search should be restarted.   * - * @param dev     the master device - * @param bdir    the bit to write if both id_bit and comp_bit are 0 - * @return        bit fields - see above + * Return:        bit fields - see above   */  u8 w1_triplet(struct w1_master *dev, int bdir)  { @@ -212,10 +235,10 @@ u8 w1_triplet(struct w1_master *dev, int bdir)  }  /** - * Reads 8 bits. + * w1_read_8() - Reads 8 bits. + * @dev:	the master device   * - * @param dev     the master device - * @return        the byte read + * Return:        the byte read   */  u8 w1_read_8(struct w1_master *dev)  { @@ -233,11 +256,10 @@ u8 w1_read_8(struct w1_master *dev)  EXPORT_SYMBOL_GPL(w1_read_8);  /** - * Writes a series of bytes. - * - * @param dev     the master device - * @param buf     pointer to the data to write - * @param len     the number of bytes to write + * w1_write_block() - Writes a series of bytes. + * @dev:	the master device + * @buf:	pointer to the data to write + * @len:	the number of bytes to write   */  void w1_write_block(struct w1_master *dev, const u8 *buf, int len)  { @@ -255,11 +277,10 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len)  EXPORT_SYMBOL_GPL(w1_write_block);  /** - * Touches a series of bytes. - * - * @param dev     the master device - * @param buf     pointer to the data to write - * @param len     the number of bytes to write + * w1_touch_block() - Touches a series of bytes. + * @dev:	the master device + * @buf:	pointer to the data to write + * @len:	the number of bytes to write   */  void w1_touch_block(struct w1_master *dev, u8 *buf, int len)  { @@ -280,12 +301,11 @@ void w1_touch_block(struct w1_master *dev, u8 *buf, int len)  EXPORT_SYMBOL_GPL(w1_touch_block);  /** - * Reads a series of bytes. - * - * @param dev     the master device - * @param buf     pointer to the buffer to fill - * @param len     the number of bytes to read - * @return        the number of bytes read + * w1_read_block() - Reads a series of bytes. + * @dev:	the master device + * @buf:	pointer to the buffer to fill + * @len:	the number of bytes to read + * Return:	the number of bytes read   */  u8 w1_read_block(struct w1_master *dev, u8 *buf, int len)  { @@ -305,14 +325,16 @@ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len)  EXPORT_SYMBOL_GPL(w1_read_block);  /** - * Issues a reset bus sequence. - * - * @param  dev The bus master pointer - * @return     0=Device present, 1=No device present or error + * w1_reset_bus() - Issues a reset bus sequence. + * @dev:	the master device + * Return:	0=Device present, 1=No device present or error   */  int w1_reset_bus(struct w1_master *dev)  {  	int result; +	unsigned long flags = 0; + +	if(w1_disable_irqs) local_irq_save(flags);  	if (dev->bus_master->reset_bus)  		result = dev->bus_master->reset_bus(dev->bus_master->data) & 0x1; @@ -325,19 +347,21 @@ int w1_reset_bus(struct w1_master *dev)  		 * cpu for such a short amount of time AND get it back in  		 * the maximum amount of time.  		 */ -		w1_delay(480); +		w1_delay(500);  		dev->bus_master->write_bit(dev->bus_master->data, 1);  		w1_delay(70);  		result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1; -		/* minmum 70 (above) + 410 = 480 us +		/* minmum 70 (above) + 430 = 500 us  		 * There aren't any timing requirements between a reset and  		 * the following transactions.  Sleeping is safe here.  		 */ -		/* w1_delay(410); min required time */ +		/* w1_delay(430); min required time */  		msleep(1);  	} +	if(w1_disable_irqs) local_irq_restore(flags); +  	return result;  }  EXPORT_SYMBOL_GPL(w1_reset_bus); @@ -364,12 +388,15 @@ void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_cal  }  /** + * w1_reset_select_slave() - reset and select a slave + * @sl:		the slave to select + *   * Resets the bus and then selects the slave by sending either a skip rom - * or a rom match. + * or a rom match.  A skip rom is issued if there is only one device + * registered on the bus.   * The w1 master lock must be held.   * - * @param sl	the slave to select - * @return 	0=success, anything else=error + * Return:	0=success, anything else=error   */  int w1_reset_select_slave(struct w1_slave *sl)  { @@ -390,6 +417,37 @@ int w1_reset_select_slave(struct w1_slave *sl)  EXPORT_SYMBOL_GPL(w1_reset_select_slave);  /** + * w1_reset_resume_command() - resume instead of another match ROM + * @dev:	the master device + * + * When the workflow with a slave amongst many requires several + * successive commands a reset between each, this function is similar + * to doing a reset then a match ROM for the last matched ROM. The + * advantage being that the matched ROM step is skipped in favor of the + * resume command. The slave must support the command of course. + * + * If the bus has only one slave, traditionnaly the match ROM is skipped + * and a "SKIP ROM" is done for efficiency. On multi-slave busses, this + * doesn't work of course, but the resume command is the next best thing. + * + * The w1 master lock must be held. + */ +int w1_reset_resume_command(struct w1_master *dev) +{ +	if (w1_reset_bus(dev)) +		return -1; + +	/* This will make only the last matched slave perform a skip ROM. */ +	w1_write_8(dev, W1_RESUME_CMD); +	return 0; +} +EXPORT_SYMBOL_GPL(w1_reset_resume_command); + +/** + * w1_next_pullup() - register for a strong pullup + * @dev:	the master device + * @delay:	time in milliseconds + *   * Put out a strong pull-up of the specified duration after the next write   * operation.  Not all hardware supports strong pullups.  Hardware that   * doesn't support strong pullups will sleep for the given time after the @@ -397,8 +455,7 @@ EXPORT_SYMBOL_GPL(w1_reset_select_slave);   * the next write, specifying zero will clear a previous request.   * The w1 master lock must be held.   * - * @param delay	time in milliseconds - * @return	0=success, anything else=error + * Return:	0=success, anything else=error   */  void w1_next_pullup(struct w1_master *dev, int delay)  { diff --git a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h index e6ab7cf08f8..9c7bd62e6bd 100644 --- a/drivers/w1/w1_log.h +++ b/drivers/w1/w1_log.h @@ -1,7 +1,7 @@  /*   *	w1_log.h   * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 7e667bc77ef..351a2978ba7 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -1,7 +1,7 @@  /*   * w1_netlink.c   * - * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -29,125 +29,294 @@  #include "w1_netlink.h"  #if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE))) -void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) -{ -	char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)]; -	struct cn_msg *m = (struct cn_msg *)buf; -	struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1); - -	memset(buf, 0, sizeof(buf)); -	m->id.idx = CN_W1_IDX; -	m->id.val = CN_W1_VAL; +#define MIN(a, b)                   (((a) < (b)) ? (a) : (b)) -	m->seq = dev->seq++; -	m->len = sizeof(struct w1_netlink_msg); - -	memcpy(w, msg, sizeof(struct w1_netlink_msg)); +/* Bundle together everything required to process a request in one memory + * allocation. + */ +struct w1_cb_block { +	atomic_t refcnt; +	u32 portid; /* Sending process port ID */ +	/* maximum value for first_cn->len */ +	u16 maxlen; +	/* pointers to building up the reply message */ +	struct cn_msg *first_cn; /* fixed once the structure is populated */ +	struct cn_msg *cn; /* advances as cn_msg is appeneded */ +	struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */ +	struct w1_netlink_cmd *cmd; /* advances as cmds are appened */ +	struct w1_netlink_msg *cur_msg; /* currently message being processed */ +	/* copy of the original request follows */ +	struct cn_msg request_cn; +	/* followed by variable length: +	 * cn_msg, data (w1_netlink_msg and w1_netlink_cmd) +	 * one or more struct w1_cb_node +	 * reply first_cn, data (w1_netlink_msg and w1_netlink_cmd) +	 */ +}; +struct w1_cb_node { +	struct w1_async_cmd async; +	/* pointers within w1_cb_block and cn data */ +	struct w1_cb_block *block; +	struct w1_netlink_msg *msg; +	struct w1_slave *sl; +	struct w1_master *dev; +}; -	cn_netlink_send(m, 0, GFP_KERNEL); +/** + * w1_reply_len() - calculate current reply length, compare to maxlen + * @block: block to calculate + * + * Calculates the current message length including possible multiple + * cn_msg and data, excludes the first sizeof(struct cn_msg).  Direclty + * compariable to maxlen and usable to send the message. + */ +static u16 w1_reply_len(struct w1_cb_block *block) +{ +	if (!block->cn) +		return 0; +	return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len;  } -static void w1_send_slave(struct w1_master *dev, u64 rn) +static void w1_unref_block(struct w1_cb_block *block)  { -	struct cn_msg *msg = dev->priv; -	struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); -	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); -	int avail; +	if (atomic_sub_return(1, &block->refcnt) == 0) { +		u16 len = w1_reply_len(block); +		if (len) { +			cn_netlink_send_mult(block->first_cn, len, +				block->portid, 0, GFP_KERNEL); +		} +		kfree(block); +	} +} -	avail = dev->priv_size - cmd->len; +/** + * w1_reply_make_space() - send message if needed to make space + * @block: block to make space on + * @space: how many bytes requested + * + * Verify there is enough room left for the caller to add "space" bytes to the + * message, if there isn't send the message and reset. + */ +static void w1_reply_make_space(struct w1_cb_block *block, u16 space) +{ +	u16 len = w1_reply_len(block); +	if (len + space >= block->maxlen) { +		cn_netlink_send_mult(block->first_cn, len, block->portid, 0, GFP_KERNEL); +		block->first_cn->len = 0; +		block->cn = NULL; +		block->msg = NULL; +		block->cmd = NULL; +	} +} -	if (avail > 8) { -		u64 *data = (void *)(cmd + 1) + cmd->len; +/* Early send when replies aren't bundled. */ +static void w1_netlink_check_send(struct w1_cb_block *block) +{ +	if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn) +		w1_reply_make_space(block, block->maxlen); +} -		*data = rn; -		cmd->len += 8; -		hdr->len += 8; -		msg->len += 8; -		return; +/** + * w1_netlink_setup_msg() - prepare to write block->msg + * @block: block to operate on + * @ack: determines if cn can be reused + * + * block->cn will be setup with the correct ack, advancing if needed + * block->cn->len does not include space for block->msg + * block->msg advances but remains uninitialized + */ +static void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack) +{ +	if (block->cn && block->cn->ack == ack) { +		block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len); +	} else { +		/* advance or set to data */ +		if (block->cn) +			block->cn = (struct cn_msg *)(block->cn->data + +				block->cn->len); +		else +			block->cn = block->first_cn; + +		memcpy(block->cn, &block->request_cn, sizeof(*block->cn)); +		block->cn->len = 0; +		block->cn->ack = ack; +		block->msg = (struct w1_netlink_msg *)block->cn->data;  	} +} -	msg->ack++; -	cn_netlink_send(msg, 0, GFP_KERNEL); +/* Append cmd to msg, include cmd->data as well.  This is because + * any following data goes with the command and in the case of a read is + * the results. + */ +static void w1_netlink_queue_cmd(struct w1_cb_block *block, +	struct w1_netlink_cmd *cmd) +{ +	u32 space; +	w1_reply_make_space(block, sizeof(struct cn_msg) + +		sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len); + +	/* There's a status message sent after each command, so no point +	 * in trying to bundle this cmd after an existing one, because +	 * there won't be one.  Allocate and copy over a new cn_msg. +	 */ +	w1_netlink_setup_msg(block, block->request_cn.seq + 1); +	memcpy(block->msg, block->cur_msg, sizeof(*block->msg)); +	block->cn->len += sizeof(*block->msg); +	block->msg->len = 0; +	block->cmd = (struct w1_netlink_cmd *)(block->msg->data); + +	space = sizeof(*cmd) + cmd->len; +	if (block->cmd != cmd) +		memcpy(block->cmd, cmd, space); +	block->cn->len += space; +	block->msg->len += space; +} -	msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); -	hdr->len = sizeof(struct w1_netlink_cmd); -	cmd->len = 0; +/* Append req_msg and req_cmd, no other commands and no data from req_cmd are + * copied. + */ +static void w1_netlink_queue_status(struct w1_cb_block *block, +	struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd, +	int error) +{ +	u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd); +	w1_reply_make_space(block, space); +	w1_netlink_setup_msg(block, block->request_cn.ack); + +	memcpy(block->msg, req_msg, sizeof(*req_msg)); +	block->cn->len += sizeof(*req_msg); +	block->msg->len = 0; +	block->msg->status = (u8)-error; +	if (req_cmd) { +		struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data; +		memcpy(cmd, req_cmd, sizeof(*cmd)); +		block->cn->len += sizeof(*cmd); +		block->msg->len += sizeof(*cmd); +		cmd->len = 0; +	} +	w1_netlink_check_send(block);  } -static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, -		unsigned int avail) +/** + * w1_netlink_send_error() - sends the error message now + * @cn: original cn_msg + * @msg: original w1_netlink_msg + * @portid: where to send it + * @error: error status + * + * Use when a block isn't available to queue the message to and cn, msg + * might not be contiguous. + */ +static void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg, +	int portid, int error)  { -	struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); -	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); -	int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH; +	struct { +		struct cn_msg cn; +		struct w1_netlink_msg msg; +	} packet; +	memcpy(&packet.cn, cn, sizeof(packet.cn)); +	memcpy(&packet.msg, msg, sizeof(packet.msg)); +	packet.cn.len = sizeof(packet.msg); +	packet.msg.len = 0; +	packet.msg.status = (u8)-error; +	cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL); +} -	dev->priv = msg; -	dev->priv_size = avail; +/** + * w1_netlink_send() - sends w1 netlink notifications + * @dev: w1_master the even is associated with or for + * @msg: w1_netlink_msg message to be sent + * + * This are notifications generated from the kernel. + */ +void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) +{ +	struct { +		struct cn_msg cn; +		struct w1_netlink_msg msg; +	} packet; +	memset(&packet, 0, sizeof(packet)); -	w1_search_devices(dev, search_type, w1_send_slave); +	packet.cn.id.idx = CN_W1_IDX; +	packet.cn.id.val = CN_W1_VAL; -	msg->ack = 0; -	cn_netlink_send(msg, 0, GFP_KERNEL); +	packet.cn.seq = dev->seq++; +	packet.cn.len = sizeof(*msg); -	dev->priv = NULL; -	dev->priv_size = 0; +	memcpy(&packet.msg, msg, sizeof(*msg)); +	packet.msg.len = 0; -	return 0; +	cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);  } -static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, -		struct w1_netlink_cmd *cmd) +static void w1_send_slave(struct w1_master *dev, u64 rn)  { -	void *data; -	struct w1_netlink_msg *h; -	struct w1_netlink_cmd *c; -	struct cn_msg *cm; -	int err; - -	data = kzalloc(sizeof(struct cn_msg) + -			sizeof(struct w1_netlink_msg) + -			sizeof(struct w1_netlink_cmd) + -			cmd->len, GFP_KERNEL); -	if (!data) -		return -ENOMEM; +	struct w1_cb_block *block = dev->priv; +	struct w1_netlink_cmd *cache_cmd = block->cmd; +	u64 *data; -	cm = (struct cn_msg *)(data); -	h = (struct w1_netlink_msg *)(cm + 1); -	c = (struct w1_netlink_cmd *)(h + 1); +	w1_reply_make_space(block, sizeof(*data)); -	memcpy(cm, msg, sizeof(struct cn_msg)); -	memcpy(h, hdr, sizeof(struct w1_netlink_msg)); -	memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); +	/* Add cmd back if the packet was sent */ +	if (!block->cmd) { +		cache_cmd->len = 0; +		w1_netlink_queue_cmd(block, cache_cmd); +	} -	cm->ack = msg->seq+1; -	cm->len = sizeof(struct w1_netlink_msg) + -		sizeof(struct w1_netlink_cmd) + cmd->len; +	data = (u64 *)(block->cmd->data + block->cmd->len); -	h->len = sizeof(struct w1_netlink_cmd) + cmd->len; +	*data = rn; +	block->cn->len += sizeof(*data); +	block->msg->len += sizeof(*data); +	block->cmd->len += sizeof(*data); +} -	memcpy(c->data, cmd->data, c->len); +static void w1_found_send_slave(struct w1_master *dev, u64 rn) +{ +	/* update kernel slave list */ +	w1_slave_found(dev, rn); -	err = cn_netlink_send(cm, 0, GFP_KERNEL); +	w1_send_slave(dev, rn); +} -	kfree(data); +/* Get the current slave list, or search (with or without alarm) */ +static int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd) +{ +	struct w1_slave *sl; -	return err; +	req_cmd->len = 0; +	w1_netlink_queue_cmd(dev->priv, req_cmd); + +	if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { +		u64 rn; +		mutex_lock(&dev->list_mutex); +		list_for_each_entry(sl, &dev->slist, w1_slave_entry) { +			memcpy(&rn, &sl->reg_num, sizeof(rn)); +			w1_send_slave(dev, rn); +		} +		mutex_unlock(&dev->list_mutex); +	} else { +		w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ? +			W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); +	} + +	return 0;  } -static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, -		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static int w1_process_command_io(struct w1_master *dev, +	struct w1_netlink_cmd *cmd)  {  	int err = 0;  	switch (cmd->cmd) {  	case W1_CMD_TOUCH:  		w1_touch_block(dev, cmd->data, cmd->len); -		w1_send_read_reply(msg, hdr, cmd); +		w1_netlink_queue_cmd(dev->priv, cmd);  		break;  	case W1_CMD_READ:  		w1_read_block(dev, cmd->data, cmd->len); -		w1_send_read_reply(msg, hdr, cmd); +		w1_netlink_queue_cmd(dev->priv, cmd);  		break;  	case W1_CMD_WRITE:  		w1_write_block(dev, cmd->data, cmd->len); @@ -160,79 +329,97 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,  	return err;  } -static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg, -		struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd) +static int w1_process_command_addremove(struct w1_master *dev, +	struct w1_netlink_cmd *cmd)  { -	int err = -EINVAL; -	struct cn_msg *msg; -	struct w1_netlink_msg *hdr; -	struct w1_netlink_cmd *cmd; +	struct w1_slave *sl; +	int err = 0; +	struct w1_reg_num *id; -	msg = kzalloc(PAGE_SIZE, GFP_KERNEL); -	if (!msg) -		return -ENOMEM; +	if (cmd->len != sizeof(*id)) +		return -EINVAL; -	msg->id = req_msg->id; -	msg->seq = req_msg->seq; -	msg->ack = 0; -	msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); +	id = (struct w1_reg_num *)cmd->data; -	hdr = (struct w1_netlink_msg *)(msg + 1); -	cmd = (struct w1_netlink_cmd *)(hdr + 1); +	sl = w1_slave_search_device(dev, id); +	switch (cmd->cmd) { +	case W1_CMD_SLAVE_ADD: +		if (sl) +			err = -EINVAL; +		else +			err = w1_attach_slave_device(dev, id); +		break; +	case W1_CMD_SLAVE_REMOVE: +		if (sl) +			w1_slave_detach(sl); +		else +			err = -EINVAL; +		break; +	default: +		err = -EINVAL; +		break; +	} -	hdr->type = W1_MASTER_CMD; -	hdr->id = req_hdr->id; -	hdr->len = sizeof(struct w1_netlink_cmd); +	return err; +} -	cmd->cmd = req_cmd->cmd; -	cmd->len = 0; +static int w1_process_command_master(struct w1_master *dev, +	struct w1_netlink_cmd *req_cmd) +{ +	int err = -EINVAL; -	switch (cmd->cmd) { +	/* drop bus_mutex for search (does it's own locking), and add/remove +	 * which doesn't use the bus +	 */ +	switch (req_cmd->cmd) {  	case W1_CMD_SEARCH:  	case W1_CMD_ALARM_SEARCH: -		err = w1_process_search_command(dev, msg, -				PAGE_SIZE - msg->len - sizeof(struct cn_msg)); +	case W1_CMD_LIST_SLAVES: +		mutex_unlock(&dev->bus_mutex); +		err = w1_get_slaves(dev, req_cmd); +		mutex_lock(&dev->bus_mutex);  		break;  	case W1_CMD_READ:  	case W1_CMD_WRITE:  	case W1_CMD_TOUCH: -		err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd); +		err = w1_process_command_io(dev, req_cmd);  		break;  	case W1_CMD_RESET:  		err = w1_reset_bus(dev);  		break; +	case W1_CMD_SLAVE_ADD: +	case W1_CMD_SLAVE_REMOVE: +		mutex_unlock(&dev->bus_mutex); +		mutex_lock(&dev->mutex); +		err = w1_process_command_addremove(dev, req_cmd); +		mutex_unlock(&dev->mutex); +		mutex_lock(&dev->bus_mutex); +		break;  	default:  		err = -EINVAL;  		break;  	} -	kfree(msg);  	return err;  } -static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, -		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static int w1_process_command_slave(struct w1_slave *sl, +		struct w1_netlink_cmd *cmd)  {  	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",  		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,  		sl->reg_num.crc, cmd->cmd, cmd->len); -	return w1_process_command_io(sl->master, msg, hdr, cmd); +	return w1_process_command_io(sl->master, cmd);  } -static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) +static int w1_process_command_root(struct cn_msg *req_cn, u32 portid)  { -	struct w1_master *m; +	struct w1_master *dev;  	struct cn_msg *cn; -	struct w1_netlink_msg *w; +	struct w1_netlink_msg *msg;  	u32 *id; -	if (mcmd->type != W1_LIST_MASTERS) { -		printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n", -			__func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len); -		return -EPROTO; -	} -  	cn = kmalloc(PAGE_SIZE, GFP_KERNEL);  	if (!cn)  		return -ENOMEM; @@ -240,108 +427,264 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc  	cn->id.idx = CN_W1_IDX;  	cn->id.val = CN_W1_VAL; -	cn->seq = msg->seq; -	cn->ack = 1; +	cn->seq = req_cn->seq; +	cn->ack = req_cn->seq + 1;  	cn->len = sizeof(struct w1_netlink_msg); -	w = (struct w1_netlink_msg *)(cn + 1); +	msg = (struct w1_netlink_msg *)cn->data; -	w->type = W1_LIST_MASTERS; -	w->status = 0; -	w->len = 0; -	id = (u32 *)(w + 1); +	msg->type = W1_LIST_MASTERS; +	msg->status = 0; +	msg->len = 0; +	id = (u32 *)msg->data;  	mutex_lock(&w1_mlock); -	list_for_each_entry(m, &w1_masters, w1_master_entry) { +	list_for_each_entry(dev, &w1_masters, w1_master_entry) {  		if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { -			cn_netlink_send(cn, 0, GFP_KERNEL); -			cn->ack++; +			cn_netlink_send(cn, portid, 0, GFP_KERNEL);  			cn->len = sizeof(struct w1_netlink_msg); -			w->len = 0; -			id = (u32 *)(w + 1); +			msg->len = 0; +			id = (u32 *)msg->data;  		} -		*id = m->id; -		w->len += sizeof(*id); +		*id = dev->id; +		msg->len += sizeof(*id);  		cn->len += sizeof(*id);  		id++;  	} -	cn->ack = 0; -	cn_netlink_send(cn, 0, GFP_KERNEL); +	cn_netlink_send(cn, portid, 0, GFP_KERNEL);  	mutex_unlock(&w1_mlock);  	kfree(cn);  	return 0;  } -static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, -		struct w1_netlink_cmd *rcmd, int error) +static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)  { -	struct cn_msg *cmsg; -	struct w1_netlink_msg *msg; -	struct w1_netlink_cmd *cmd; +	struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node, +		async); +	u16 mlen = node->msg->len; +	u16 len; +	int err = 0; +	struct w1_slave *sl = node->sl; +	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data; -	cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL); -	if (!cmsg) -		return -ENOMEM; +	mutex_lock(&dev->bus_mutex); +	dev->priv = node->block; +	if (sl && w1_reset_select_slave(sl)) +		err = -ENODEV; +	node->block->cur_msg = node->msg; -	msg = (struct w1_netlink_msg *)(cmsg + 1); -	cmd = (struct w1_netlink_cmd *)(msg + 1); +	while (mlen && !err) { +		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { +			err = -E2BIG; +			break; +		} -	memcpy(cmsg, rcmsg, sizeof(*cmsg)); -	cmsg->len = sizeof(*msg); +		if (sl) +			err = w1_process_command_slave(sl, cmd); +		else +			err = w1_process_command_master(dev, cmd); +		w1_netlink_check_send(node->block); -	memcpy(msg, rmsg, sizeof(*msg)); -	msg->len = 0; -	msg->status = (short)-error; +		w1_netlink_queue_status(node->block, node->msg, cmd, err); +		err = 0; -	if (rcmd) { -		memcpy(cmd, rcmd, sizeof(*cmd)); -		cmd->len = 0; -		msg->len += sizeof(*cmd); -		cmsg->len += sizeof(*cmd); +		len = sizeof(*cmd) + cmd->len; +		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len); +		mlen -= len;  	} -	error = cn_netlink_send(cmsg, 0, GFP_KERNEL); -	kfree(cmsg); +	if (!cmd || err) +		w1_netlink_queue_status(node->block, node->msg, cmd, err); + +	/* ref taken in w1_search_slave or w1_search_master_id when building +	 * the block +	 */ +	if (sl) +		w1_unref_slave(sl); +	else +		atomic_dec(&dev->refcnt); +	dev->priv = NULL; +	mutex_unlock(&dev->bus_mutex); + +	mutex_lock(&dev->list_mutex); +	list_del(&async_cmd->async_entry); +	mutex_unlock(&dev->list_mutex); + +	w1_unref_block(node->block); +} + +static void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count, +	u16 *slave_len) +{ +	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data; +	u16 mlen = msg->len; +	u16 len; +	int slave_list = 0; +	while (mlen) { +		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) +			break; + +		switch (cmd->cmd) { +		case W1_CMD_SEARCH: +		case W1_CMD_ALARM_SEARCH: +		case W1_CMD_LIST_SLAVES: +			++slave_list; +		} +		++*cmd_count; +		len = sizeof(*cmd) + cmd->len; +		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len); +		mlen -= len; +	} -	return error; +	if (slave_list) { +		struct w1_master *dev = w1_search_master_id(msg->id.mst.id); +		if (dev) { +			/* Bytes, and likely an overstimate, and if it isn't +			 * the results can still be split between packets. +			 */ +			*slave_len += sizeof(struct w1_reg_num) * slave_list * +				(dev->slave_count + dev->max_slave_count); +			/* search incremented it */ +			atomic_dec(&dev->refcnt); +		} +	}  } -static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)  { -	struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); -	struct w1_netlink_cmd *cmd; +	struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);  	struct w1_slave *sl;  	struct w1_master *dev; +	u16 msg_len; +	u16 slave_len = 0;  	int err = 0; +	struct w1_cb_block *block = NULL; +	struct w1_cb_node *node = NULL; +	int node_count = 0; +	int cmd_count = 0; + +	/* If any unknown flag is set let the application know, that way +	 * applications can detect the absence of features in kernels that +	 * don't know about them.  http://lwn.net/Articles/587527/ +	 */ +	if (cn->flags & ~(W1_CN_BUNDLE)) { +		w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL); +		return; +	} -	while (msg->len && !err) { -		struct w1_reg_num id; -		u16 mlen = m->len; -		u8 *cmd_data = m->data; +	/* Count the number of master or slave commands there are to allocate +	 * space for one cb_node each. +	 */ +	msg_len = cn->len; +	while (msg_len && !err) { +		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) { +			err = -E2BIG; +			break; +		} + +		/* count messages for nodes and allocate any additional space +		 * required for slave lists +		 */ +		if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) { +			++node_count; +			w1_list_count_cmds(msg, &cmd_count, &slave_len); +		} + +		msg_len -= sizeof(struct w1_netlink_msg) + msg->len; +		msg = (struct w1_netlink_msg *)(((u8 *)msg) + +			sizeof(struct w1_netlink_msg) + msg->len); +	} +	msg = (struct w1_netlink_msg *)(cn + 1); +	if (node_count) { +		int size; +		u16 reply_size = sizeof(*cn) + cn->len + slave_len; +		if (cn->flags & W1_CN_BUNDLE) { +			/* bundling duplicats some of the messages */ +			reply_size += 2 * cmd_count * (sizeof(struct cn_msg) + +				sizeof(struct w1_netlink_msg) + +				sizeof(struct w1_netlink_cmd)); +		} +		reply_size = MIN(CONNECTOR_MAX_MSG_SIZE, reply_size); + +		/* allocate space for the block, a copy of the original message, +		 * one node per cmd to point into the original message, +		 * space for replies which is the original message size plus +		 * space for any list slave data and status messages +		 * cn->len doesn't include itself which is part of the block +		 * */ +		size =  /* block + original message */ +			sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len + +			/* space for nodes */ +			node_count * sizeof(struct w1_cb_node) + +			/* replies */ +			sizeof(struct cn_msg) + reply_size; +		block = kzalloc(size, GFP_KERNEL); +		if (!block) { +			/* if the system is already out of memory, +			 * (A) will this work, and (B) would it be better +			 * to not try? +			 */ +			w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM); +			return; +		} +		atomic_set(&block->refcnt, 1); +		block->portid = nsp->portid; +		memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len); +		node = (struct w1_cb_node *)(block->request_cn.data + cn->len); + +		/* Sneeky, when not bundling, reply_size is the allocated space +		 * required for the reply, cn_msg isn't part of maxlen so +		 * it should be reply_size - sizeof(struct cn_msg), however +		 * when checking if there is enough space, w1_reply_make_space +		 * is called with the full message size including cn_msg, +		 * because it isn't known at that time if an additional cn_msg +		 * will need to be allocated.  So an extra cn_msg is added +		 * above in "size". +		 */ +		block->maxlen = reply_size; +		block->first_cn = (struct cn_msg *)(node + node_count); +		memset(block->first_cn, 0, sizeof(*block->first_cn)); +	} + +	msg_len = cn->len; +	while (msg_len && !err) {  		dev = NULL;  		sl = NULL; -		cmd = NULL; -		memcpy(&id, m->id.id, sizeof(id)); -#if 0 -		printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", -				__func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); -#endif -		if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { +		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {  			err = -E2BIG;  			break;  		} -		if (m->type == W1_MASTER_CMD) { -			dev = w1_search_master_id(m->id.mst.id); -		} else if (m->type == W1_SLAVE_CMD) { -			sl = w1_search_slave(&id); +		/* execute on this thread, no need to process later */ +		if (msg->type == W1_LIST_MASTERS) { +			err = w1_process_command_root(cn, nsp->portid); +			goto out_cont; +		} + +		/* All following message types require additional data, +		 * check here before references are taken. +		 */ +		if (!msg->len) { +			err = -EPROTO; +			goto out_cont; +		} + +		/* both search calls take references */ +		if (msg->type == W1_MASTER_CMD) { +			dev = w1_search_master_id(msg->id.mst.id); +		} else if (msg->type == W1_SLAVE_CMD) { +			sl = w1_search_slave((struct w1_reg_num *)msg->id.id);  			if (sl)  				dev = sl->master;  		} else { -			err = w1_process_command_root(msg, m); +			printk(KERN_NOTICE +				"%s: cn: %x.%x, wrong type: %u, len: %u.\n", +				__func__, cn->id.idx, cn->id.val, +				msg->type, msg->len); +			err = -EPROTO;  			goto out_cont;  		} @@ -351,45 +694,31 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)  		}  		err = 0; -		if (!mlen) -			goto out_cont; - -		mutex_lock(&dev->mutex); - -		if (sl && w1_reset_select_slave(sl)) { -			err = -ENODEV; -			goto out_up; -		} - -		while (mlen) { -			cmd = (struct w1_netlink_cmd *)cmd_data; -			if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { -				err = -E2BIG; -				break; -			} +		atomic_inc(&block->refcnt); +		node->async.cb = w1_process_cb; +		node->block = block; +		node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn + +			(size_t)((u8 *)msg - (u8 *)cn)); +		node->sl = sl; +		node->dev = dev; -			if (sl) -				err = w1_process_command_slave(sl, msg, m, cmd); -			else -				err = w1_process_command_master(dev, msg, m, cmd); - -			w1_netlink_send_error(msg, m, cmd, err); -			err = 0; +		mutex_lock(&dev->list_mutex); +		list_add_tail(&node->async.async_entry, &dev->async_list); +		wake_up_process(dev->thread); +		mutex_unlock(&dev->list_mutex); +		++node; -			cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); -			mlen -= cmd->len + sizeof(struct w1_netlink_cmd); -		} -out_up: -		atomic_dec(&dev->refcnt); -		if (sl) -			atomic_dec(&sl->refcnt); -		mutex_unlock(&dev->mutex);  out_cont: -		if (!cmd || err) -			w1_netlink_send_error(msg, m, cmd, err); -		msg->len -= sizeof(struct w1_netlink_msg) + m->len; -		m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); +		/* Can't queue because that modifies block and another +		 * thread could be processing the messages by now and +		 * there isn't a lock, send directly. +		 */ +		if (err) +			w1_netlink_send_error(cn, msg, nsp->portid, err); +		msg_len -= sizeof(struct w1_netlink_msg) + msg->len; +		msg = (struct w1_netlink_msg *)(((u8 *)msg) + +			sizeof(struct w1_netlink_msg) + msg->len);  		/*  		 * Let's allow requests for nonexisting devices. @@ -397,6 +726,8 @@ out_cont:  		if (err == -ENODEV)  			err = 0;  	} +	if (block) +		w1_unref_block(block);  }  int w1_init_netlink(void) @@ -413,7 +744,7 @@ void w1_fini_netlink(void)  	cn_del_callback(&w1_id);  }  #else -void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) +void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn)  {  } diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index 27e950f935b..c99a9ce05e6 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -1,7 +1,7 @@  /*   * w1_netlink.h   * - * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru> + * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>   *   *   * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,29 @@  #include "w1.h" +/** + * enum w1_cn_msg_flags - bitfield flags for struct cn_msg.flags + * + * @W1_CN_BUNDLE: Request bundling replies into fewer messagse.  Be prepared + * to handle multiple struct cn_msg, struct w1_netlink_msg, and + * struct w1_netlink_cmd in one packet. + */ +enum w1_cn_msg_flags { +	W1_CN_BUNDLE = 1, +}; + +/** + * enum w1_netlink_message_types - message type + * + * @W1_SLAVE_ADD: notification that a slave device was added + * @W1_SLAVE_REMOVE: notification that a slave device was removed + * @W1_MASTER_ADD: notification that a new bus master was added + * @W1_MASTER_REMOVE: notification that a bus masterwas removed + * @W1_MASTER_CMD: initiate operations on a specific master + * @W1_SLAVE_CMD: sends reset, selects the slave, then does a read/write/touch + * operation + * @W1_LIST_MASTERS: used to determine the bus master identifiers + */  enum w1_netlink_message_types {  	W1_SLAVE_ADD = 0,  	W1_SLAVE_REMOVE, @@ -37,6 +60,19 @@ enum w1_netlink_message_types {  	W1_LIST_MASTERS,  }; +/** + * struct w1_netlink_msg - holds w1 message type, id, and result + * + * @type: one of enum w1_netlink_message_types + * @status: kernel feedback for success 0 or errno failure value + * @len: length of data following w1_netlink_msg + * @id: union holding master bus id (msg.id) and slave device id (id[8]). + * @data: start address of any following data + * + * The base message structure for w1 messages over netlink. + * The netlink connector data sequence is, struct nlmsghdr, struct cn_msg, + * then one or more struct w1_netlink_msg (each with optional data). + */  struct w1_netlink_msg  {  	__u8				type; @@ -52,6 +88,23 @@ struct w1_netlink_msg  	__u8				data[0];  }; +/** + * enum w1_commands - commands available for master or slave operations + * + * @W1_CMD_READ: read len bytes + * @W1_CMD_WRITE: write len bytes + * @W1_CMD_SEARCH: initiate a standard search, returns only the slave + * devices found during that search + * @W1_CMD_ALARM_SEARCH: search for devices that are currently alarming + * @W1_CMD_TOUCH: Touches a series of bytes. + * @W1_CMD_RESET: sends a bus reset on the given master + * @W1_CMD_SLAVE_ADD: adds a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_SLAVE_REMOVE: removes a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_LIST_SLAVES: list of slaves registered on this master + * @W1_CMD_MAX: number of available commands + */  enum w1_commands {  	W1_CMD_READ = 0,  	W1_CMD_WRITE, @@ -59,9 +112,23 @@ enum w1_commands {  	W1_CMD_ALARM_SEARCH,  	W1_CMD_TOUCH,  	W1_CMD_RESET, -	W1_CMD_MAX, +	W1_CMD_SLAVE_ADD, +	W1_CMD_SLAVE_REMOVE, +	W1_CMD_LIST_SLAVES, +	W1_CMD_MAX  }; +/** + * struct w1_netlink_cmd - holds the command and data + * + * @cmd: one of enum w1_commands + * @res: reserved + * @len: length of data following w1_netlink_cmd + * @data: start address of any following data + * + * One or more struct w1_netlink_cmd is placed starting at w1_netlink_msg.data + * each with optional data. + */  struct w1_netlink_cmd  {  	__u8				cmd;  | 
