diff options
author | Takashi Iwai <tiwai@suse.de> | 2008-12-20 23:39:47 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-12-20 23:39:47 +0100 |
commit | 55fa518867978e1f5fd8353098f80d125ac734d7 (patch) | |
tree | 3502b331c1f9ec4cac25dc8ba30b6a0a324e350c /drivers/w1 | |
parent | bb1f24bf00a85f666b56a09b7cdbfd221af16c2c (diff) | |
parent | eea0579fc85e64e9f05361d5aacf496fe7a151aa (diff) |
Merge branch 'topic/pcsp-fix' into topic/misc
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/masters/Kconfig | 7 | ||||
-rw-r--r-- | drivers/w1/masters/Makefile | 1 | ||||
-rw-r--r-- | drivers/w1/masters/ds1wm.c | 10 | ||||
-rw-r--r-- | drivers/w1/masters/ds2490.c | 348 | ||||
-rw-r--r-- | drivers/w1/masters/omap_hdq.c | 725 | ||||
-rw-r--r-- | drivers/w1/slaves/Kconfig | 7 | ||||
-rw-r--r-- | drivers/w1/slaves/Makefile | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_bq27000.c | 123 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2431.c | 312 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2760.c | 1 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 72 | ||||
-rw-r--r-- | drivers/w1/w1.c | 423 | ||||
-rw-r--r-- | drivers/w1/w1.h | 35 | ||||
-rw-r--r-- | drivers/w1/w1_family.c | 13 | ||||
-rw-r--r-- | drivers/w1/w1_family.h | 3 | ||||
-rw-r--r-- | drivers/w1/w1_int.c | 88 | ||||
-rw-r--r-- | drivers/w1/w1_io.c | 88 |
17 files changed, 1868 insertions, 390 deletions
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index c4493091c65..a14d5b6e4c7 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -52,5 +52,12 @@ config W1_MASTER_GPIO This support is also available as a module. If so, the module will be called w1-gpio.ko. +config HDQ_MASTER_OMAP + tristate "OMAP HDQ driver" + depends on ARCH_OMAP2430 || ARCH_OMAP34XX + help + Say Y here if you want support for the 1-wire or HDQ Interface + on an OMAP processor. + endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index 1420b5bbdda..bc4714a75f3 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o +obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 10211e49300..29e144f81cb 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -160,8 +160,10 @@ static int ds1wm_reset(struct ds1wm_data *ds1wm_data) * 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. */ - udelay(500); + msleep(1); ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD | @@ -274,8 +276,8 @@ static u8 ds1wm_reset_bus(void *data) return 0; } -static void ds1wm_search(void *data, u8 search_type, - w1_slave_found_callback slave_found) +static void ds1wm_search(void *data, struct w1_master *master_dev, + u8 search_type, w1_slave_found_callback slave_found) { struct ds1wm_data *ds1wm_data = data; int i; @@ -313,7 +315,7 @@ static void ds1wm_search(void *data, u8 search_type, ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); ds1wm_reset(ds1wm_data); - slave_found(ds1wm_data, rom_id); + slave_found(master_dev, rom_id); } /* --------------------------------------------------------------------- */ diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index b63b5e044a4..59ad6e95af8 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -88,7 +88,7 @@ #define COMM_DT 0x2000 #define COMM_SPU 0x1000 #define COMM_F 0x0800 -#define COMM_NTP 0x0400 +#define COMM_NTF 0x0400 #define COMM_ICP 0x0200 #define COMM_RST 0x0100 @@ -98,11 +98,6 @@ #define BRANCH_MAIN 0xCC #define BRANCH_AUX 0x33 -/* - * Duration of the strong pull-up pulse in milliseconds. - */ -#define PULLUP_PULSE_DURATION 750 - /* Status flags */ #define ST_SPUA 0x01 /* Strong Pull-up is active */ #define ST_PRGA 0x02 /* 12V programming pulse is being generated */ @@ -112,6 +107,17 @@ #define ST_IDLE 0x20 /* DS2490 is currently idle */ #define ST_EPOF 0x80 +/* Result Register flags */ +#define RR_DETECT 0xA5 /* New device detected */ +#define RR_NRS 0x01 /* Reset no presence or ... */ +#define RR_SH 0x02 /* short on reset or set path */ +#define RR_APP 0x04 /* alarming presence on reset */ +#define RR_VPP 0x08 /* 12V expected not seen */ +#define RR_CMP 0x10 /* compare error */ +#define RR_CRC 0x20 /* CRC error detected */ +#define RR_RDP 0x40 /* redirected page */ +#define RR_EOS 0x80 /* end of search error */ + #define SPEED_NORMAL 0x00 #define SPEED_FLEXIBLE 0x01 #define SPEED_OVERDRIVE 0x02 @@ -131,6 +137,15 @@ struct ds_device int ep[NUM_EP]; + /* Strong PullUp + * 0: pullup not active, else duration in milliseconds + */ + int spu_sleep; + /* spu_bit contains COMM_SPU or 0 depending on if the strong pullup + * should be active or not for writes. + */ + u16 spu_bit; + struct w1_bus_master master; }; @@ -164,7 +179,6 @@ MODULE_DEVICE_TABLE(usb, ds_id_table); static int ds_probe(struct usb_interface *, const struct usb_device_id *); static void ds_disconnect(struct usb_interface *); -static inline void ds_dump_status(unsigned char *, unsigned char *, int); static int ds_send_control(struct ds_device *, u16, u16); static int ds_send_control_cmd(struct ds_device *, u16, u16); @@ -192,7 +206,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) return err; } -#if 0 + static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) { int err; @@ -207,7 +221,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) return err; } -#endif + static int ds_send_control(struct ds_device *dev, u16 value, u16 index) { int err; @@ -223,11 +237,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) return err; } -static inline void ds_dump_status(unsigned char *buf, unsigned char *str, int off) -{ - printk("%45s: %8x\n", str, buf[off]); -} - static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, unsigned char *buf, int size) { @@ -248,62 +257,81 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, return count; } -static int ds_recv_status(struct ds_device *dev, struct ds_status *st) +static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) { - unsigned char buf[64]; - int count, err = 0, i; - - memcpy(st, buf, sizeof(*st)); + printk(KERN_INFO "%45s: %8x\n", str, buf[off]); +} - count = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); - if (count < 0) - return err; +static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) +{ + int i; - printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], count); + printk(KERN_INFO "0x%x: count=%d, status: ", dev->ep[EP_STATUS], count); for (i=0; i<count; ++i) printk("%02x ", buf[i]); - printk("\n"); + printk(KERN_INFO "\n"); if (count >= 16) { - ds_dump_status(buf, "enable flag", 0); - ds_dump_status(buf, "1-wire speed", 1); - ds_dump_status(buf, "strong pullup duration", 2); - ds_dump_status(buf, "programming pulse duration", 3); - ds_dump_status(buf, "pulldown slew rate control", 4); - ds_dump_status(buf, "write-1 low time", 5); - ds_dump_status(buf, "data sample offset/write-0 recovery time", 6); - ds_dump_status(buf, "reserved (test register)", 7); - ds_dump_status(buf, "device status flags", 8); - ds_dump_status(buf, "communication command byte 1", 9); - ds_dump_status(buf, "communication command byte 2", 10); - ds_dump_status(buf, "communication command buffer status", 11); - ds_dump_status(buf, "1-wire data output buffer status", 12); - ds_dump_status(buf, "1-wire data input buffer status", 13); - ds_dump_status(buf, "reserved", 14); - ds_dump_status(buf, "reserved", 15); + ds_print_msg(buf, "enable flag", 0); + ds_print_msg(buf, "1-wire speed", 1); + ds_print_msg(buf, "strong pullup duration", 2); + ds_print_msg(buf, "programming pulse duration", 3); + ds_print_msg(buf, "pulldown slew rate control", 4); + ds_print_msg(buf, "write-1 low time", 5); + ds_print_msg(buf, "data sample offset/write-0 recovery time", + 6); + ds_print_msg(buf, "reserved (test register)", 7); + ds_print_msg(buf, "device status flags", 8); + ds_print_msg(buf, "communication command byte 1", 9); + ds_print_msg(buf, "communication command byte 2", 10); + ds_print_msg(buf, "communication command buffer status", 11); + ds_print_msg(buf, "1-wire data output buffer status", 12); + ds_print_msg(buf, "1-wire data input buffer status", 13); + ds_print_msg(buf, "reserved", 14); + ds_print_msg(buf, "reserved", 15); } - - memcpy(st, buf, sizeof(*st)); - - if (st->status & ST_EPOF) { - printk(KERN_INFO "Resetting device after ST_EPOF.\n"); - err = ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); - if (err) - return err; - count = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); - if (count < 0) - return err; - } -#if 0 - if (st->status & ST_IDLE) { - printk(KERN_INFO "Resetting pulse after ST_IDLE.\n"); - err = ds_start_pulse(dev, PULLUP_PULSE_DURATION); - if (err) - return err; + for (i = 16; i < count; ++i) { + if (buf[i] == RR_DETECT) { + ds_print_msg(buf, "new device detect", i); + continue; + } + ds_print_msg(buf, "Result Register Value: ", i); + if (buf[i] & RR_NRS) + printk(KERN_INFO "NRS: Reset no presence or ...\n"); + if (buf[i] & RR_SH) + printk(KERN_INFO "SH: short on reset or set path\n"); + if (buf[i] & RR_APP) + printk(KERN_INFO "APP: alarming presence on reset\n"); + if (buf[i] & RR_VPP) + printk(KERN_INFO "VPP: 12V expected not seen\n"); + if (buf[i] & RR_CMP) + printk(KERN_INFO "CMP: compare error\n"); + if (buf[i] & RR_CRC) + printk(KERN_INFO "CRC: CRC error detected\n"); + if (buf[i] & RR_RDP) + printk(KERN_INFO "RDP: redirected page\n"); + if (buf[i] & RR_EOS) + printk(KERN_INFO "EOS: end of search error\n"); } -#endif +} - return err; +static void ds_reset_device(struct ds_device *dev) +{ + ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); + /* Always allow strong pullup which allow individual writes to use + * the strong pullup. + */ + if (ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE)) + printk(KERN_ERR "ds_reset_device: " + "Error allowing strong pullup\n"); + /* Chip strong pullup time was cleared. */ + if (dev->spu_sleep) { + /* lower 4 bits are 0, see ds_set_pullup */ + u8 del = dev->spu_sleep>>4; + if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del)) + printk(KERN_ERR "ds_reset_device: " + "Error setting duration\n"); + } } static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) @@ -311,13 +339,27 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) int count, err; struct ds_status st; + /* Careful on size. If size is less than what is available in + * the input buffer, the device fails the bulk transfer and + * clears the input buffer. It could read the maximum size of + * the data buffer, but then do you return the first, last, or + * some set of the middle size bytes? As long as the rest of + * the code is correct there will be size bytes waiting. A + * call to ds_wait_status will wait until the device is idle + * and any data to be received would have been available. + */ count = 0; 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]; + int count; + printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); - ds_recv_status(dev, &st); + + count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); + ds_dump_status(dev, buf, count); return err; } @@ -341,7 +383,8 @@ static int ds_send_data(struct ds_device *dev, unsigned char *buf, int len) count = 0; err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000); if (err < 0) { - printk(KERN_ERR "Failed to read 1-wire data from 0x02: err=%d.\n", err); + printk(KERN_ERR "Failed to write 1-wire data to ep0x%x: " + "err=%d.\n", dev->ep[EP_DATA_OUT], err); return err; } @@ -397,7 +440,7 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) if (err) return err; - err = ds_recv_status(dev, st); + err = ds_dump_status(dev, st); return err; } @@ -420,33 +463,49 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) printk("\n"); } #endif - } while(!(buf[0x08] & 0x20) && !(err < 0) && ++count < 100); + } while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100); + + if (err >= 16 && st->status & ST_EPOF) { + printk(KERN_INFO "Resetting device after ST_EPOF.\n"); + ds_reset_device(dev); + /* Always dump the device status. */ + count = 101; + } + /* Dump the status for errors or if there is extended return data. + * The extended status includes new device detection (maybe someone + * can do something with it). + */ + if (err > 16 || count >= 100 || err < 0) + ds_dump_status(dev, buf, err); - if (((err > 16) && (buf[0x10] & 0x01)) || count >= 100 || err < 0) { - ds_recv_status(dev, st); + /* Extended data isn't an error. Well, a short is, but the dump + * would have already told the user that and we can't do anything + * about it in software anyway. + */ + if (count >= 100 || err < 0) return -1; - } else + else return 0; } -static int ds_reset(struct ds_device *dev, struct ds_status *st) +static int ds_reset(struct ds_device *dev) { int err; - //err = ds_send_control(dev, COMM_1_WIRE_RESET | COMM_F | COMM_IM | COMM_SE, SPEED_FLEXIBLE); - err = ds_send_control(dev, 0x43, SPEED_NORMAL); + /* Other potentionally interesting flags for reset. + * + * COMM_NTF: Return result register feedback. This could be used to + * detect some conditions such as short, alarming presence, or + * detect if a new device was detected. + * + * COMM_SE which allows SPEED_NORMAL, SPEED_FLEXIBLE, SPEED_OVERDRIVE: + * Select the data transfer rate. + */ + err = ds_send_control(dev, COMM_1_WIRE_RESET | COMM_IM, SPEED_NORMAL); if (err) return err; - ds_wait_status(dev, st); -#if 0 - if (st->command_buffer_status) { - printk(KERN_INFO "Short circuit.\n"); - return -EIO; - } -#endif - return 0; } @@ -471,60 +530,43 @@ static int ds_set_speed(struct ds_device *dev, int speed) } #endif /* 0 */ -static int ds_start_pulse(struct ds_device *dev, int delay) +static int ds_set_pullup(struct ds_device *dev, int delay) { - int err; + int err = 0; u8 del = 1 + (u8)(delay >> 4); - struct ds_status st; - -#if 0 - err = ds_stop_pulse(dev, 10); - if (err) + /* Just storing delay would not get the trunication and roundup. */ + int ms = del<<4; + + /* Enable spu_bit if a delay is set. */ + dev->spu_bit = delay ? COMM_SPU : 0; + /* If delay is zero, it has already been disabled, if the time is + * the same as the hardware was last programmed to, there is also + * nothing more to do. Compare with the recalculated value ms + * rather than del or delay which can have a different value. + */ + if (delay == 0 || ms == dev->spu_sleep) return err; - err = ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE); - if (err) - return err; -#endif err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del); if (err) return err; - err = ds_send_control(dev, COMM_PULSE | COMM_IM | COMM_F, 0); - if (err) - return err; - - mdelay(delay); - - ds_wait_status(dev, &st); + dev->spu_sleep = ms; return err; } static int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit) { - int err, count; + int err; struct ds_status st; - u16 value = (COMM_BIT_IO | COMM_IM) | ((bit) ? COMM_D : 0); - u16 cmd; - err = ds_send_control(dev, value, 0); + err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | (bit ? COMM_D : 0), + 0); if (err) return err; - count = 0; - do { - err = ds_wait_status(dev, &st); - if (err) - return err; - - cmd = st.command0 | (st.command1 << 8); - } while (cmd != value && ++count < 10); - - if (err < 0 || count >= 10) { - printk(KERN_ERR "Failed to obtain status.\n"); - return -EINVAL; - } + ds_wait_status(dev, &st); err = ds_recv_data(dev, tbit, sizeof(*tbit)); if (err < 0) @@ -533,12 +575,18 @@ static int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit) return 0; } +#if 0 static int ds_write_bit(struct ds_device *dev, u8 bit) { int err; struct ds_status st; - err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | (bit) ? COMM_D : 0, 0); + /* Set COMM_ICP to write without a readback. Note, this will + * produce one time slot, a down followed by an up with COMM_D + * only determing the timing. + */ + err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | COMM_ICP | + (bit ? COMM_D : 0), 0); if (err) return err; @@ -546,6 +594,7 @@ static int ds_write_bit(struct ds_device *dev, u8 bit) return 0; } +#endif static int ds_write_byte(struct ds_device *dev, u8 byte) { @@ -553,10 +602,13 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) struct ds_status st; u8 rbyte; - err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | COMM_SPU, byte); + err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte); if (err) return err; + if (dev->spu_bit) + msleep(dev->spu_sleep); + err = ds_wait_status(dev, &st); if (err) return err; @@ -565,8 +617,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) if (err < 0) return err; - ds_start_pulse(dev, PULLUP_PULSE_DURATION); - return !(byte == rbyte); } @@ -602,7 +652,7 @@ static int ds_read_block(struct ds_device *dev, u8 *buf, int len) if (err < 0) return err; - err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | COMM_SPU, len); + err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM, len); if (err) return err; @@ -623,20 +673,19 @@ static int ds_write_block(struct ds_device *dev, u8 *buf, int len) if (err < 0) return err; - ds_wait_status(dev, &st); - - err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | COMM_SPU, len); + err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | dev->spu_bit, len); if (err) return err; + if (dev->spu_bit) + msleep(dev->spu_sleep); + ds_wait_status(dev, &st); err = ds_recv_data(dev, buf, len); if (err < 0) return err; - ds_start_pulse(dev, PULLUP_PULSE_DURATION); - return !(err == len); } @@ -728,6 +777,7 @@ static u8 ds9490r_touch_bit(void *data, u8 bit) return ret; } +#if 0 static void ds9490r_write_bit(void *data, u8 bit) { struct ds_device *dev = data; @@ -735,13 +785,6 @@ static void ds9490r_write_bit(void *data, u8 bit) ds_write_bit(dev, bit); } -static void ds9490r_write_byte(void *data, u8 byte) -{ - struct ds_device *dev = data; - - ds_write_byte(dev, byte); -} - static u8 ds9490r_read_bit(void *data) { struct ds_device *dev = data; @@ -754,6 +797,14 @@ static u8 ds9490r_read_bit(void *data) return bit & 1; } +#endif + +static void ds9490r_write_byte(void *data, u8 byte) +{ + struct ds_device *dev = data; + + ds_write_byte(dev, byte); +} static u8 ds9490r_read_byte(void *data) { @@ -790,31 +841,58 @@ static u8 ds9490r_read_block(void *data, u8 *buf, int len) static u8 ds9490r_reset(void *data) { struct ds_device *dev = data; - struct ds_status st; int err; - memset(&st, 0, sizeof(st)); - - err = ds_reset(dev, &st); + err = ds_reset(dev); if (err) return 1; return 0; } +static u8 ds9490r_set_pullup(void *data, int delay) +{ + struct ds_device *dev = data; + + if (ds_set_pullup(dev, delay)) + return 1; + + return 0; +} + static int ds_w1_init(struct ds_device *dev) { memset(&dev->master, 0, sizeof(struct w1_bus_master)); + /* Reset the device as it can be in a bad state. + * This is necessary because a block write will wait for data + * to be placed in the output buffer and block any later + * commands which will keep accumulating and the device will + * not be idle. Another case is removing the ds2490 module + * while a bus search is in progress, somehow a few commands + * get through, but the input transfers fail leaving data in + * the input buffer. This will cause the next read to fail + * see the note in ds_recv_data. + */ + ds_reset_device(dev); + dev->master.data = dev; dev->master.touch_bit = &ds9490r_touch_bit; + /* read_bit and write_bit in w1_bus_master are expected to set and + * sample the line level. For write_bit that means it is expected to + * set it to that value and leave it there. ds2490 only supports an + * individual time slot at the lowest level. The requirement from + * pulling the bus state down to reading the state is 15us, something + * that isn't realistic on the USB bus anyway. dev->master.read_bit = &ds9490r_read_bit; dev->master.write_bit = &ds9490r_write_bit; + */ dev->master.read_byte = &ds9490r_read_byte; dev->master.write_byte = &ds9490r_write_byte; dev->master.read_block = &ds9490r_read_block; dev->master.write_block = &ds9490r_write_block; dev->master.reset_bus = &ds9490r_reset; + dev->master.set_pullup = &ds9490r_set_pullup; return w1_add_master_device(&dev->master); } @@ -838,6 +916,8 @@ static int ds_probe(struct usb_interface *intf, 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; diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c new file mode 100644 index 00000000000..c973889110c --- /dev/null +++ b/drivers/w1/masters/omap_hdq.c @@ -0,0 +1,725 @@ +/* + * drivers/w1/masters/omap_hdq.c + * + * Copyright (C) 2007 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 + * kind, whether express or implied. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +#include "../w1.h" +#include "../w1_int.h" + +#define MOD_NAME "OMAP_HDQ:" + +#define OMAP_HDQ_REVISION 0x00 +#define OMAP_HDQ_TX_DATA 0x04 +#define OMAP_HDQ_RX_DATA 0x08 +#define OMAP_HDQ_CTRL_STATUS 0x0c +#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK (1<<6) +#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE (1<<5) +#define OMAP_HDQ_CTRL_STATUS_GO (1<<4) +#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION (1<<2) +#define OMAP_HDQ_CTRL_STATUS_DIR (1<<1) +#define OMAP_HDQ_CTRL_STATUS_MODE (1<<0) +#define OMAP_HDQ_INT_STATUS 0x10 +#define OMAP_HDQ_INT_STATUS_TXCOMPLETE (1<<2) +#define OMAP_HDQ_INT_STATUS_RXCOMPLETE (1<<1) +#define OMAP_HDQ_INT_STATUS_TIMEOUT (1<<0) +#define OMAP_HDQ_SYSCONFIG 0x14 +#define OMAP_HDQ_SYSCONFIG_SOFTRESET (1<<1) +#define OMAP_HDQ_SYSCONFIG_AUTOIDLE (1<<0) +#define OMAP_HDQ_SYSSTATUS 0x18 +#define OMAP_HDQ_SYSSTATUS_RESETDONE (1<<0) + +#define OMAP_HDQ_FLAG_CLEAR 0 +#define OMAP_HDQ_FLAG_SET 1 +#define OMAP_HDQ_TIMEOUT (HZ/5) + +#define OMAP_HDQ_MAX_USER 4 + +static DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue); +static int w1_id; + +struct hdq_data { + struct device *dev; + void __iomem *hdq_base; + /* 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; + /* + * Used to control the call to omap_hdq_get and omap_hdq_put. + * HDQ Protocol: Write the CMD|REG_address first, followed by + * the data wrire or read. + */ + int init_trans; +}; + +static int __init omap_hdq_probe(struct platform_device *pdev); +static int omap_hdq_remove(struct platform_device *pdev); + +static struct platform_driver omap_hdq_driver = { + .probe = omap_hdq_probe, + .remove = omap_hdq_remove, + .driver = { + .name = "omap_hdq", + }, +}; + +static u8 omap_w1_read_byte(void *_hdq); +static void omap_w1_write_byte(void *_hdq, u8 byte); +static u8 omap_w1_reset_bus(void *_hdq); +static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev, + u8 search_type, w1_slave_found_callback slave_found); + + +static struct w1_bus_master omap_w1_master = { + .read_byte = omap_w1_read_byte, + .write_byte = omap_w1_write_byte, + .reset_bus = omap_w1_reset_bus, + .search = omap_w1_search_bus, +}; + +/* 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); +} + +static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) +{ + __raw_writeb(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) + | (val & mask); + __raw_writeb(new_val, hdq_data->hdq_base + offset); + + return new_val; +} + +/* + * Wait for one or more bits in flag change. + * HDQ_FLAG_SET: wait until any bit in the flag is set. + * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared. + * return 0 on success and -ETIMEDOUT in the case of timeout. + */ +static int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset, + u8 flag, u8 flag_set, u8 *status) +{ + int ret = 0; + unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; + + if (flag_set == OMAP_HDQ_FLAG_CLEAR) { + /* wait for the flag clear */ + while (((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (*status & flag) + ret = -ETIMEDOUT; + } else if (flag_set == OMAP_HDQ_FLAG_SET) { + /* wait for the flag set */ + while (!((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (!(*status & flag)) + ret = -ETIMEDOUT; + } else + return -EINVAL; + + return ret; +} + +/* write out a byte and fill *status with HDQ_INT_STATUS */ +static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) +{ + int ret; + u8 tmp_status; + unsigned long irqflags; + + *status = 0; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val); + + /* set the GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); + /* wait for the TXCOMPLETE bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX wait elapsed\n"); + goto out; + } + + *status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) { + dev_dbg(hdq_data->dev, "timeout waiting for" + "TXCOMPLETE/RXCOMPLETE, %x", *status); + ret = -ETIMEDOUT; + goto out; + } + + /* wait for the GO bit return to zero */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_FLAG_CLEAR, &tmp_status); + if (ret) { + dev_dbg(hdq_data->dev, "timeout waiting GO bit" + "return to zero, %x", tmp_status); + } + +out: + return ret; +} + +/* HDQ Interrupt service routine */ +static irqreturn_t hdq_isr(int irq, void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + unsigned long irqflags; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + hdq_data->hdq_irqstatus = hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + dev_dbg(hdq_data->dev, "hdq_isr: %x", hdq_data->hdq_irqstatus); + + if (hdq_data->hdq_irqstatus & + (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE + | OMAP_HDQ_INT_STATUS_TIMEOUT)) { + /* wake up sleeping process */ + wake_up(&hdq_wait_queue); + } + + return IRQ_HANDLED; +} + +/* HDQ Mode: always return success */ +static u8 omap_w1_reset_bus(void *_hdq) +{ + return 0; +} + +/* W1 search callback function */ +static void omap_w1_search_bus(void *_hdq, struct w1_master *master_dev, + u8 search_type, w1_slave_found_callback slave_found) +{ + u64 module_id, rn_le, cs, id; + + if (w1_id) + module_id = w1_id; + else + module_id = 0x1; + + rn_le = cpu_to_le64(module_id); + /* + * HDQ might not obey truly the 1-wire spec. + * So calculate CRC based on module parameter. + */ + cs = w1_calc_crc8((u8 *)&rn_le, 7); + id = (cs << 56) | module_id; + + slave_found(master_dev, id); +} + +static int _omap_hdq_reset(struct hdq_data *hdq_data) +{ + int ret; + u8 tmp_status; + + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_SOFTRESET); + /* + * Select HDQ mode & enable clocks. + * It is observed that INT flags can't be cleared via a read and GO/INIT + * won't return to zero if interrupt is disabled. So we always enable + * interrupt. + */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + + /* wait for reset to complete */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_SYSSTATUS, + OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting HDQ reset, %x", + tmp_status); + else { + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + } + + return ret; +} + +/* Issue break pulse to the device */ +static int omap_hdq_break(struct hdq_data *hdq_data) +{ + int ret = 0; + u8 tmp_status; + unsigned long irqflags; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + ret = -EINTR; + goto rtn; + } + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + /* set the INIT and GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO); + + /* wait for the TIMEOUT bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "break wait elapsed\n"); + ret = -EINTR; + goto out; + } + + tmp_status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) { + dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x", + tmp_status); + ret = -ETIMEDOUT; + goto out; + } + /* + * wait for both INIT and GO bits rerurn to zero. + * zero wait time expected for interrupt mode. + */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR, + &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" + "return to zero, %x", tmp_status); + +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +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) { + ret = -EINTR; + goto rtn; + } + + if (!hdq_data->hdq_usecount) { + ret = -EINVAL; + goto out; + } + + if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + 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. + */ + while (!(hdq_data->hdq_irqstatus + & OMAP_HDQ_INT_STATUS_RXCOMPLETE) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + 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); + ret = -ETIMEDOUT; + goto out; + } + } + /* the data is ready. Read it in! */ + *val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return 0; + +} + +/* Enable clocks and set the controller to HDQ mode */ +static int omap_hdq_get(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to exceed the max use count"); + ret = -EINVAL; + goto out; + } else { + 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; + } + + /* make sure HDQ is out of reset */ + if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & + OMAP_HDQ_SYSSTATUS_RESETDONE)) { + ret = _omap_hdq_reset(hdq_data); + if (ret) + /* back up the count */ + hdq_data->hdq_usecount--; + } else { + /* select HDQ mode & enable clocks */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + } + } + } + +clk_err: + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +/* Disable clocks to the module */ +static int omap_hdq_put(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) + return -EINTR; + + if (0 == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to decrement use count" + "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); + } + } + mutex_unlock(&hdq_data->hdq_mutex); + + return ret; +} + +/* Read a byte of data from the device */ +static u8 omap_w1_read_byte(void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + u8 val = 0; + int ret; + + ret = hdq_read_byte(hdq_data, &val); + if (ret) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + return -1; + } + + /* Write followed by a read, release the module */ + if (hdq_data->init_trans) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + } + + return val; +} + +/* Write a byte of data to the device */ +static void omap_w1_write_byte(void *_hdq, u8 byte) +{ + struct hdq_data *hdq_data = _hdq; + int ret; + u8 status; + + /* First write to initialize the transfer */ + if (hdq_data->init_trans == 0) + omap_hdq_get(hdq_data); + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans++; + mutex_unlock(&hdq_data->hdq_mutex); + + ret = hdq_write_byte(hdq_data, byte, &status); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status); + return; + } + + /* Second write, data transfered. Release the module */ + if (hdq_data->init_trans > 1) { + omap_hdq_put(hdq_data); + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + } + + return; +} + +static int __init omap_hdq_probe(struct platform_device *pdev) +{ + struct hdq_data *hdq_data; + struct resource *res; + int ret, irq; + u8 rev; + + hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL); + if (!hdq_data) { + dev_dbg(&pdev->dev, "unable to allocate memory\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + hdq_data->dev = &pdev->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, "hdq_ick"); + hdq_data->hdq_fck = clk_get(&pdev->dev, "hdq_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_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; + } + + rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); + dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", + (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt"); + + spin_lock_init(&hdq_data->hdq_spinlock); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENXIO; + goto err_irq; + } + + ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data); + if (ret < 0) { + dev_dbg(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + + 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); + + omap_w1_master.data = hdq_data; + + ret = w1_add_master_device(&omap_w1_master); + if (ret) { + dev_dbg(&pdev->dev, "Failure in registering w1 master\n"); + goto err_w1; + } + + 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); + +err_kmalloc: + return ret; + +} + +static int omap_hdq_remove(struct platform_device *pdev) +{ + struct hdq_data *hdq_data = platform_get_drvdata(pdev); + + mutex_lock(&hdq_data->hdq_mutex); + + if (hdq_data->hdq_usecount) { + dev_dbg(&pdev->dev, "removed when use count is not zero\n"); + return -EBUSY; + } + + 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); + + 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_param(w1_id, int, S_IRUSR); +MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection"); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("HDQ driver Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3df29a122f8..8d0b1fb1e52 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -44,4 +44,11 @@ config W1_SLAVE_DS2760 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. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index a8eb7524df1..990f400b6d2 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o - +obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c new file mode 100644 index 00000000000..8f4c91f6c68 --- /dev/null +++ b/drivers/w1/slaves/w1_bq27000.c @@ -0,0 +1,123 @@ +/* + * drivers/w1/slaves/w1_bq27000.c + * + * Copyright (C) 2007 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 + * kind, whether express or implied. + * + */ + +#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 "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define HDQ_CMD_READ (0) +#define HDQ_CMD_WRITE (1<<7) + +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) +{ + u8 val; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return 0; + + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + + return val; +} +EXPORT_SYMBOL(w1_bq27000_read); + +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); + if (!pdev) { + ret = -ENOMEM; + return ret; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + goto success; + +pdev_add_failed: + platform_device_unregister(pdev); +success: + return ret; +} + +static void w1_bq27000_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + + platform_device_unregister(pdev); +} + +static struct w1_family_ops w1_bq27000_fops = { + .add_slave = w1_bq27000_add_slave, + .remove_slave = w1_bq27000_remove_slave, +}; + +static struct w1_family w1_bq27000_family = { + .fid = 1, + .fops = &w1_bq27000_fops, +}; + +static int __init w1_bq27000_init(void) +{ + if (F_ID) + w1_bq27000_family.fid = F_ID; + + return w1_register_family(&w1_bq27000_family); +} + +static void __exit w1_bq27000_exit(void) +{ + w1_unregister_family(&w1_bq27000_family); +} + + +module_init(w1_bq27000_init); +module_exit(w1_bq27000_exit); + +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c new file mode 100644 index 00000000000..2c6c0cf6a20 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2431.c @@ -0,0 +1,312 @@ +/* + * w1_ds2431.c - w1 family 2d (DS2431) driver + * + * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net> + * + * Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.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 "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define W1_F2D_EEPROM_SIZE 128 +#define W1_F2D_PAGE_COUNT 4 +#define W1_F2D_PAGE_BITS 5 +#define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS) +#define W1_F2D_PAGE_MASK 0x1F + +#define W1_F2D_SCRATCH_BITS 3 +#define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS) +#define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1) + +#define W1_F2D_READ_EEPROM 0xF0 +#define W1_F2D_WRITE_SCRATCH 0x0F +#define W1_F2D_READ_SCRATCH 0xAA +#define W1_F2D_COPY_SCRATCH 0x55 + + +#define W1_F2D_TPROG_MS 11 + +#define W1_F2D_READ_RETRIES 10 +#define W1_F2D_READ_MAXLEN 8 + +/* + * 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_f2d_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; +} + +/* + * Read a block from W1 ROM two times and compares the results. + * If they are equal they are returned, otherwise the read + * is repeated W1_F2D_READ_RETRIES times. + * + * count must not exceed W1_F2D_READ_MAXLEN. + */ +static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) +{ + u8 wrbuf[3]; + u8 cmp[W1_F2D_READ_MAXLEN]; + int tries = W1_F2D_READ_RETRIES; + + do { + wrbuf[0] = W1_F2D_READ_EEPROM; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, buf, count); + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, cmp, count); + + if (!memcmp(cmp, buf, count)) + return 0; + } while (--tries); + + dev_err(&sl->dev, "proof reading failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; +} + +static ssize_t w1_f2d_read_bin(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; + + count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->mutex); + + /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */ + while (todo > 0) { + int block_read; + + if (todo >= W1_F2D_READ_MAXLEN) + block_read = W1_F2D_READ_MAXLEN; + else + block_read = todo; + + if (w1_f2d_readblock(sl, off, block_read, buf) < 0) + count = -EIO; + + todo -= W1_F2D_READ_MAXLEN; + buf += W1_F2D_READ_MAXLEN; + off += W1_F2D_READ_MAXLEN; + } + + 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 aligned at W1_F2D_SCRATCH_SIZE bytes and + * must be W1_F2D_SCRATCH_SIZE bytes long. + * The master must be locked. + * + * @param sl The slave structure + * @param addr Address for the write + * @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK)) + * @param data The data to write + * @return 0=Success -1=failure + */ +static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data) +{ + int tries = W1_F2D_READ_RETRIES; + u8 wrbuf[4]; + u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3]; + u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE; + +retry: + + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F2D_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_F2D_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)) { + + if (--tries) + goto retry; + + dev_err(&sl->dev, + "could not write to eeprom, scratchpad compare failed %d times\n", + W1_F2D_READ_RETRIES); + + return -1; + } + + /* Copy the scratchpad to EEPROM */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F2D_COPY_SCRATCH; + wrbuf[3] = es; + w1_write_block(sl->master, wrbuf, 4); + + /* Sleep for tprog ms to wait for the write to complete */ + msleep(W1_F2D_TPROG_MS); + + /* Reset the bus to wake up the EEPROM */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t w1_f2d_write_bin(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; + int copy; + + count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->mutex); + + /* Can only write data in blocks of the size of the scratchpad */ + addr = off; + len = count; + while (len > 0) { + + /* if len too short or addr not aligned */ + if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) { + char tmp[W1_F2D_SCRATCH_SIZE]; + + /* read the block and update the parts to be written */ + if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp)) { + count = -EIO; + goto out_up; + } + + /* copy at most to the boundary of the PAGE or len */ + copy = W1_F2D_SCRATCH_SIZE - + (addr & W1_F2D_SCRATCH_MASK); + + if (copy > len) + copy = len; + + memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy); + if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK, + W1_F2D_SCRATCH_SIZE, tmp) < 0) { + count = -EIO; + goto out_up; + } + } else { + + copy = W1_F2D_SCRATCH_SIZE; + if (w1_f2d_write(sl, addr, copy, buf) < 0) { + count = -EIO; + goto out_up; + } + } + buf += copy; + addr += copy; + len -= copy; + } + +out_up: + mutex_unlock(&sl->master->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 int w1_f2d_add_slave(struct w1_slave *sl) +{ + return sysfs_create_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); +} + +static void w1_f2d_remove_slave(struct w1_slave *sl) +{ + sysfs_remove_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); +} + +static struct w1_family_ops w1_f2d_fops = { + .add_slave = w1_f2d_add_slave, + .remove_slave = w1_f2d_remove_slave, +}; + +static struct w1_family w1_family_2d = { + .fid = W1_EEPROM_DS2431, + .fops = &w1_f2d_fops, +}; + +static int __init w1_f2d_init(void) +{ + return w1_register_family(&w1_family_2d); +} + +static void __exit w1_f2d_fini(void) +{ + w1_unregister_family(&w1_family_2d); +} + +module_init(w1_f2d_init); +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"); diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index ed6b0576208..1f09d4e4144 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -80,7 +80,6 @@ static struct bin_attribute w1_ds2760_bin_attr = { .attr = { .name = "w1_slave", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = DS2760_DATA_SIZE, .read = w1_ds2760_read_bin, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index fb28acaeed6..2c8dff9f77d 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -37,31 +37,33 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); +/* 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. + */ +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_bin(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); +static ssize_t w1_therm_read(struct device *device, + struct device_attribute *attr, char *buf); -static struct bin_attribute w1_therm_bin_attr = { - .attr = { - .name = "w1_slave", - .mode = S_IRUGO, - }, - .size = W1_SLAVE_DATA_SIZE, - .read = w1_therm_read_bin, -}; +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 sysfs_create_bin_file(&sl->dev.kobj, &w1_therm_bin_attr); + return device_create_file(&sl->dev, &w1_therm_attr); } static void w1_therm_remove_slave(struct w1_slave *sl) { - sysfs_remove_bin_file(&sl->dev.kobj, &w1_therm_bin_attr); + device_remove_file(&sl->dev, &w1_therm_attr); } static struct w1_family_ops w1_therm_fops = { @@ -160,30 +162,19 @@ static int w1_therm_check_rom(u8 rom[9]) return 0; } -static ssize_t w1_therm_read_bin(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t w1_therm_read(struct device *device, + struct device_attribute *attr, char *buf) { - struct w1_slave *sl = kobj_to_w1_slave(kobj); + struct w1_slave *sl = dev_to_w1_slave(device); struct w1_master *dev = sl->master; u8 rom[9], crc, verdict; int i, max_trying = 10; + ssize_t c = PAGE_SIZE; - mutex_lock(&sl->master->mutex); + mutex_lock(&dev->mutex); - if (off > W1_SLAVE_DATA_SIZE) { - count = 0; - goto out; - } - if (off + count > W1_SLAVE_DATA_SIZE) { - count = 0; - goto out; - } - - memset(buf, 0, count); memset(rom, 0, sizeof(rom)); - count = 0; verdict = 0; crc = 0; @@ -192,15 +183,20 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, int count = 0; unsigned int tm = 750; + /* 750ms strong pullup (or delay) after the convert */ + if (w1_strong_pullup) + w1_next_pullup(dev, tm); w1_write_8(dev, W1_CONVERT_TEMP); - - msleep(tm); + if (!w1_strong_pullup) + msleep(tm); if (!w1_reset_select_slave(sl)) { w1_write_8(dev, W1_READ_SCRATCHPAD); if ((count = w1_read_block(dev, rom, 9)) != 9) { - dev_warn(&dev->dev, "w1_read_block() returned %d instead of 9.\n", count); + dev_warn(device, "w1_read_block() " + "returned %u instead of 9.\n", + count); } crc = w1_calc_crc8(rom, 8); @@ -215,22 +211,22 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, } for (i = 0; i < 9; ++i) - count += sprintf(buf + count, "%02x ", rom[i]); - count += sprintf(buf + count, ": crc=%02x %s\n", + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", crc, (verdict) ? "YES" : "NO"); if (verdict) memcpy(sl->rom, rom, sizeof(sl->rom)); else - dev_warn(&dev->dev, "18S20 doesn't respond to CONVERT_TEMP.\n"); + dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n"); for (i = 0; i < 9; ++i) - count += sprintf(buf + count, "%02x ", sl->rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]); - count += sprintf(buf + count, "t=%d\n", w1_convert_temp(rom, sl->family->fid)); -out: + c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", + w1_convert_temp(rom, sl->family->fid)); mutex_unlock(&dev->mutex); - return count; + return PAGE_SIZE - c; } static int __init w1_therm_init(void) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 7293c9b11f9..3b615d4022e 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -46,19 +46,17 @@ MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol."); static int w1_timeout = 10; -static int w1_control_timeout = 1; int w1_max_slave_count = 10; int w1_max_slave_ttl = 10; module_param_named(timeout, w1_timeout, int, 0); -module_param_named(control_timeout, w1_control_timeout, int, 0); module_param_named(max_slave_count, w1_max_slave_count, int, 0); module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); DEFINE_MUTEX(w1_mlock); LIST_HEAD(w1_masters); -static struct task_struct *w1_control_thread; +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) { @@ -83,10 +81,10 @@ static void w1_slave_release(struct device *dev) { struct w1_slave *sl = dev_to_w1_slave(dev); - printk("%s: Releasing %s.\n", __func__, sl->name); + dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); while (atomic_read(&sl->refcnt)) { - printk("Waiting for %s to become free: refcnt=%d.\n", + 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); @@ -105,35 +103,20 @@ static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *a return sprintf(buf, "%s\n", sl->name); } -static ssize_t w1_slave_read_id(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t w1_slave_read_id(struct device *dev, + struct device_attribute *attr, char *buf) { - struct w1_slave *sl = kobj_to_w1_slave(kobj); - - if (off > 8) { - count = 0; - } else { - if (off + count > 8) - count = 8 - off; - - memcpy(buf, (u8 *)&sl->reg_num, count); - } + struct w1_slave *sl = dev_to_w1_slave(dev); + ssize_t count = sizeof(sl->reg_num); + memcpy(buf, (u8 *)&sl->reg_num, count); return count; } static struct device_attribute w1_slave_attr_name = __ATTR(name, S_IRUGO, w1_slave_read_name, NULL); - -static struct bin_attribute w1_slave_attr_bin_id = { - .attr = { - .name = "id", - .mode = S_IRUGO, - }, - .size = 8, - .read = w1_slave_read_id, -}; +static struct device_attribute w1_slave_attr_id = + __ATTR(id, S_IRUGO, w1_slave_read_id, NULL); /* Default family */ @@ -250,11 +233,16 @@ static ssize_t w1_master_attribute_store_search(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) { + long tmp; struct w1_master *md = dev_to_w1_master(dev); + if (strict_strtol(buf, 0, &tmp) == -EINVAL) + return -EINVAL; + mutex_lock(&md->mutex); - md->search_count = simple_strtol(buf, NULL, 0); + md->search_count = tmp; mutex_unlock(&md->mutex); + wake_up_process(md->thread); return count; } @@ -273,6 +261,38 @@ static ssize_t w1_master_attribute_show_search(struct device *dev, return count; } +static ssize_t w1_master_attribute_store_pullup(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long tmp; + struct w1_master *md = dev_to_w1_master(dev); + + if (strict_strtol(buf, 0, &tmp) == -EINVAL) + return -EINVAL; + + mutex_lock(&md->mutex); + md->enable_pullup = tmp; + mutex_unlock(&md->mutex); + wake_up_process(md->thread); + + return count; +} + +static ssize_t w1_master_attribute_show_pullup(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct w1_master *md = dev_to_w1_master(dev); + ssize_t count; + + mutex_lock(&md->mutex); + count = sprintf(buf, "%d\n", md->enable_pullup); + mutex_unlock(&md->mutex); + + return count; +} + static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct device_attribute *attr, char *buf) { struct w1_master *md = dev_to_w1_master(dev); @@ -324,7 +344,8 @@ static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct d return count; } -static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t w1_master_attribute_show_slaves(struct device *dev, + struct device_attribute *attr, char *buf) { struct w1_master *md = dev_to_w1_master(dev); int c = PAGE_SIZE; @@ -349,6 +370,135 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device return PAGE_SIZE - c; } +static ssize_t w1_master_attribute_show_add(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int c = PAGE_SIZE; + c -= snprintf(buf+PAGE_SIZE - c, c, + "write device id xx-xxxxxxxxxxxx to add slave\n"); + return PAGE_SIZE - c; +} + +static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, + struct w1_reg_num *rn) +{ + unsigned int family; + unsigned long long id; + int i; + u64 rn64_le; + + /* The CRC value isn't read from the user because the sysfs directory + * doesn't include it and most messages from the bus search don't + * print it either. It would be unreasonable for the user to then + * provide it. + */ + const char *error_msg = "bad slave string format, expecting " + "ff-dddddddddddd\n"; + + if (buf[2] != '-') { + dev_err(dev, "%s", error_msg); + return -EINVAL; + } + i = sscanf(buf, "%02x-%012llx", &family, &id); + if (i != 2) { + dev_err(dev, "%s", error_msg); + return -EINVAL; + } + rn->family = family; + rn->id = id; + + rn64_le = cpu_to_le64(*(u64 *)rn); + rn->crc = w1_calc_crc8((u8 *)&rn64_le, 7); + +#if 0 + dev_info(dev, "With CRC device is %02x.%012llx.%02x.\n", + rn->family, (unsigned long long)rn->id, rn->crc); +#endif + + return 0; +} + +/* Searches the slaves in the w1_master and returns a pointer or NULL. + * Note: must hold the mutex + */ +static struct w1_slave *w1_slave_search_device(struct w1_master *dev, + struct w1_reg_num *rn) +{ + struct w1_slave *sl; + 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) { + return sl; + } + } + return NULL; +} + +static ssize_t w1_master_attribute_store_add(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_master *md = dev_to_w1_master(dev); + struct w1_reg_num rn; + struct w1_slave *sl; + ssize_t result = count; + + if (w1_atoreg_num(dev, buf, count, &rn)) + return -EINVAL; + + mutex_lock(&md->mutex); + sl = w1_slave_search_device(md, &rn); + /* It would be nice to do a targeted search one the one-wire bus + * for the new device to see if it is out there or not. But the + * current search doesn't support that. + */ + if (sl) { + dev_info(dev, "Device %s already exists\n", sl->name); + result = -EINVAL; + } else { + w1_attach_slave_device(md, &rn); + } + mutex_unlock(&md->mutex); + + return result; +} + +static ssize_t w1_master_attribute_show_remove(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int c = PAGE_SIZE; + c -= snprintf(buf+PAGE_SIZE - c, c, + "write device id xx-xxxxxxxxxxxx to remove slave\n"); + return PAGE_SIZE - c; +} + +static ssize_t w1_master_attribute_store_remove(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_master *md = dev_to_w1_master(dev); + struct w1_reg_num rn; + struct w1_slave *sl; + ssize_t result = count; + + if (w1_atoreg_num(dev, buf, count, &rn)) + return -EINVAL; + + mutex_lock(&md->mutex); + sl = w1_slave_search_device(md, &rn); + if (sl) { + w1_slave_detach(sl); + } else { + dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, + (unsigned long long)rn.id); + result = -EINVAL; + } + mutex_unlock(&md->mutex); + + return result; +} + #define W1_MASTER_ATTR_RO(_name, _mode) \ struct device_attribute w1_master_attribute_##_name = \ __ATTR(w1_master_##_name, _mode, \ @@ -368,6 +518,9 @@ static W1_MASTER_ATTR_RO(attempts, S_IRUGO); static W1_MASTER_ATTR_RO(timeout, S_IRUGO); static W1_MASTER_ATTR_RO(pointer, S_IRUGO); static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO); +static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO); +static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO); +static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO); static struct attribute *w1_master_default_attrs[] = { &w1_master_attribute_name.attr, @@ -378,6 +531,9 @@ static struct attribute *w1_master_default_attrs[] = { &w1_master_attribute_timeout.attr, &w1_master_attribute_pointer.attr, &w1_master_attribute_search.attr, + &w1_master_attribute_pullup.attr, + &w1_master_attribute_add.attr, + &w1_master_attribute_remove.attr, NULL }; @@ -390,7 +546,7 @@ int w1_create_master_attributes(struct w1_master *master) return sysfs_create_group(&master->dev.kobj, &w1_master_defattr_group); } -static void w1_destroy_master_attributes(struct w1_master *master) +void w1_destroy_master_attributes(struct w1_master *master) { sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group); } @@ -479,7 +635,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) } /* Create "id" entry */ - err = sysfs_create_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id); + 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", @@ -501,7 +657,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) return 0; out_rem2: - sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id); + device_remove_file(&sl->dev, &w1_slave_attr_id); out_rem1: device_remove_file(&sl->dev, &w1_slave_attr_name); out_unreg: @@ -567,7 +723,7 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) return 0; } -static void w1_slave_detach(struct w1_slave *sl) +void w1_slave_detach(struct w1_slave *sl) { struct w1_netlink_msg msg; @@ -583,7 +739,7 @@ static void w1_slave_detach(struct w1_slave *sl) msg.type = W1_SLAVE_REMOVE; w1_netlink_send(sl->master, &msg); - sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id); + device_remove_file(&sl->dev, &w1_slave_attr_id); device_remove_file(&sl->dev, &w1_slave_attr_name); device_unregister(&sl->dev); @@ -591,24 +747,6 @@ static void w1_slave_detach(struct w1_slave *sl) kfree(sl); } -static struct w1_master *w1_search_master(void *data) -{ - struct w1_master *dev; - int found = 0; - - mutex_lock(&w1_mlock); - list_for_each_entry(dev, &w1_masters, w1_master_entry) { - if (dev->bus_master->data == data) { - found = 1; - atomic_inc(&dev->refcnt); - break; - } - } - mutex_unlock(&w1_mlock); - - return (found)?dev:NULL; -} - struct w1_master *w1_search_master_id(u32 id) { struct w1_master *dev; @@ -656,55 +794,56 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) return (found)?sl:NULL; } -void w1_reconnect_slaves(struct w1_family *f) +void w1_reconnect_slaves(struct w1_family *f, int attach) { + struct w1_slave *sl, *sln; struct w1_master *dev; mutex_lock(&w1_mlock); list_for_each_entry(dev, &w1_masters, w1_master_entry) { - dev_dbg(&dev->dev, "Reconnecting slaves in %s into new family %02x.\n", - dev->name, f->fid); - set_bit(W1_MASTER_NEED_RECONNECT, &dev->flags); + dev_dbg(&dev->dev, "Reconnecting slaves in device %s " + "for family %02x.\n", dev->name, f->fid); + mutex_lock(&dev->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 + * connected. If the family is going away, devices + * matching that family are reconneced. + */ + if ((attach && sl->family->fid == W1_FAMILY_DEFAULT + && sl->reg_num.family == f->fid) || + (!attach && sl->family->fid == f->fid)) { + struct w1_reg_num rn; + + memcpy(&rn, &sl->reg_num, sizeof(rn)); + w1_slave_detach(sl); + + w1_attach_slave_device(dev, &rn); + } + } + dev_dbg(&dev->dev, "Reconnecting slaves in device %s " + "has been finished.\n", dev->name); + mutex_unlock(&dev->mutex); } mutex_unlock(&w1_mlock); } -static void w1_slave_found(void *data, u64 rn) +static void w1_slave_found(struct w1_master *dev, u64 rn) { - int slave_count; struct w1_slave *sl; - struct list_head *ent; struct w1_reg_num *tmp; - struct w1_master *dev; u64 rn_le = cpu_to_le64(rn); - dev = w1_search_master(data); - if (!dev) { - printk(KERN_ERR "Failed to find w1 master device for data %p, " - "it is impossible.\n", data); - return; - } + atomic_inc(&dev->refcnt); tmp = (struct w1_reg_num *) &rn; - slave_count = 0; - list_for_each(ent, &dev->slist) { - - sl = list_entry(ent, struct w1_slave, w1_slave_entry); - - if (sl->reg_num.family == tmp->family && - sl->reg_num.id == tmp->id && - sl->reg_num.crc == tmp->crc) { - set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); - break; - } - - slave_count++; - } - - if (slave_count == dev->slave_count && - rn && ((rn >> 56) & 0xff) == w1_calc_crc8((u8 *)&rn_le, 7)) { - w1_attach_slave_device(dev, tmp); + sl = w1_slave_search_device(dev, tmp); + if (sl) { + set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + } else { + if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7)) + w1_attach_slave_device(dev, tmp); } atomic_dec(&dev->refcnt); @@ -779,80 +918,20 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb /* extract the direction taken & update the device number */ tmp64 = (triplet_ret >> 2); rn |= (tmp64 << i); + + if (kthread_should_stop()) { + dev_dbg(&dev->dev, "Abort w1_search\n"); + return; + } } if ( (triplet_ret & 0x03) != 0x03 ) { if ( (desc_bit == last_zero) || (last_zero < 0)) last_device = 1; desc_bit = last_zero; - cb(dev->bus_master->data, rn); - } - } -} - -static int w1_control(void *data) -{ - struct w1_slave *sl, *sln; - struct w1_master *dev, *n; - int have_to_wait = 0; - - set_freezable(); - while (!kthread_should_stop() || have_to_wait) { - have_to_wait = 0; - - try_to_freeze(); - msleep_interruptible(w1_control_timeout * 1000); - - list_for_each_entry_safe(dev, n, &w1_masters, w1_master_entry) { - if (!kthread_should_stop() && !dev->flags) - continue; - /* - * Little race: we can create thread but not set the flag. - * Get a chance for external process to set flag up. - */ - if (!dev->initialized) { - have_to_wait = 1; - continue; - } - - if (kthread_should_stop() || test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) { - set_bit(W1_MASTER_NEED_EXIT, &dev->flags); - - mutex_lock(&w1_mlock); - list_del(&dev->w1_master_entry); - mutex_unlock(&w1_mlock); - - mutex_lock(&dev->mutex); - list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { - w1_slave_detach(sl); - } - w1_destroy_master_attributes(dev); - mutex_unlock(&dev->mutex); - atomic_dec(&dev->refcnt); - continue; - } - - if (test_bit(W1_MASTER_NEED_RECONNECT, &dev->flags)) { - dev_dbg(&dev->dev, "Reconnecting slaves in device %s.\n", dev->name); - mutex_lock(&dev->mutex); - list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { - if (sl->family->fid == W1_FAMILY_DEFAULT) { - struct w1_reg_num rn; - - memcpy(&rn, &sl->reg_num, sizeof(rn)); - w1_slave_detach(sl); - - w1_attach_slave_device(dev, &rn); - } - } - dev_dbg(&dev->dev, "Reconnecting slaves in device %s has been finished.\n", dev->name); - clear_bit(W1_MASTER_NEED_RECONNECT, &dev->flags); - mutex_unlock(&dev->mutex); - } + cb(dev, rn); } } - - return 0; } void w1_search_process(struct w1_master *dev, u8 search_type) @@ -878,23 +957,29 @@ void w1_search_process(struct w1_master *dev, u8 search_type) int w1_process(void *data) { struct w1_master *dev = (struct w1_master *) data; + /* As long as w1_timeout is only set by a module parameter the sleep + * time can be calculated in jiffies once. + */ + const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); + + while (!kthread_should_stop()) { + if (dev->search_count) { + mutex_lock(&dev->mutex); + w1_search_process(dev, W1_SEARCH); + mutex_unlock(&dev->mutex); + } - while (!kthread_should_stop() && !test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) { try_to_freeze(); - msleep_interruptible(w1_timeout * 1000); + __set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop() || test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) + if (kthread_should_stop()) break; - if (!dev->initialized) - continue; - - if (dev->search_count == 0) - continue; - - mutex_lock(&dev->mutex); - w1_search_process(dev, W1_SEARCH); - mutex_unlock(&dev->mutex); + /* Only sleep when the search is active. */ + if (dev->search_count) + schedule_timeout(jtime); + else + schedule(); } atomic_dec(&dev->refcnt); @@ -932,18 +1017,13 @@ static int w1_init(void) goto err_out_master_unregister; } - w1_control_thread = kthread_run(w1_control, NULL, "w1_control"); - if (IS_ERR(w1_control_thread)) { - retval = PTR_ERR(w1_control_thread); - printk(KERN_ERR "Failed to create control thread. err=%d\n", - retval); - goto err_out_slave_unregister; - } - return 0; +#if 0 +/* For undoing the slave register if there was a step after it. */ err_out_slave_unregister: driver_unregister(&w1_slave_driver); +#endif err_out_master_unregister: driver_unregister(&w1_master_driver); @@ -959,13 +1039,12 @@ static void w1_fini(void) { struct w1_master *dev; + /* Set netlink removal messages and some cleanup */ list_for_each_entry(dev, &w1_masters, w1_master_entry) __w1_remove_master_device(dev); w1_fini_netlink(); - kthread_stop(w1_control_thread); - driver_unregister(&w1_slave_driver); driver_unregister(&w1_master_driver); bus_unregister(&w1_bus_type); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index f1df5343f4a..97304bd83ec 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -46,7 +46,6 @@ struct w1_reg_num #include "w1_family.h" #define W1_MAXNAMELEN 32 -#define W1_SLAVE_DATA_SIZE 128 #define W1_SEARCH 0xF0 #define W1_ALARM_SEARCH 0xEC @@ -77,7 +76,7 @@ struct w1_slave struct completion released; }; -typedef void (* w1_slave_found_callback)(void *, u64); +typedef void (*w1_slave_found_callback)(struct w1_master *, u64); /** @@ -142,12 +141,18 @@ struct w1_bus_master */ u8 (*reset_bus)(void *); - /** Really nice hardware can handles the different types of ROM search */ - void (*search)(void *, u8, w1_slave_found_callback); -}; + /** + * Put out a strong pull-up pulse of the specified duration. + * @return -1=Error, 0=completed + */ + u8 (*set_pullup)(void *, int); -#define W1_MASTER_NEED_EXIT 0 -#define W1_MASTER_NEED_RECONNECT 1 + /** 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); +}; struct w1_master { @@ -167,7 +172,10 @@ struct w1_master void *priv; int priv_size; - long flags; + /** 5V strong pullup enabled flag, 1 enabled, zero disabled. */ + int enable_pullup; + /** 5V strong pullup duration in milliseconds, zero disabled. */ + int pullup_duration; struct task_struct *thread; struct mutex mutex; @@ -181,19 +189,30 @@ struct w1_master }; 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); struct w1_slave *w1_search_slave(struct w1_reg_num *id); void w1_search_process(struct w1_master *dev, u8 search_type); struct w1_master *w1_search_master_id(u32 id); +/* Disconnect and reconnect devices in the given family. Used for finding + * unclaimed devices after a family has been registered or releasing devices + * after a family has been unregistered. Set attach to 1 when a new family + * 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); + u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); +u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); u8 w1_calc_crc8(u8 *, int); void w1_write_block(struct w1_master *, const u8 *, int); u8 w1_read_block(struct w1_master *, u8 *, int); int w1_reset_select_slave(struct w1_slave *sl); +void w1_next_pullup(struct w1_master *, int); static inline struct w1_slave* dev_to_w1_slave(struct device *dev) { diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index a3c95bd6890..4a099041f28 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -48,12 +48,12 @@ int w1_register_family(struct w1_family *newf) if (!ret) { atomic_set(&newf->refcnt, 0); - newf->need_exit = 0; list_add_tail(&newf->family_entry, &w1_families); } spin_unlock(&w1_flock); - w1_reconnect_slaves(newf); + /* check default devices against the new set of drivers */ + w1_reconnect_slaves(newf, 1); return ret; } @@ -72,11 +72,11 @@ void w1_unregister_family(struct w1_family *fent) break; } } - - fent->need_exit = 1; - spin_unlock(&w1_flock); + /* deatch devices using this family code */ + w1_reconnect_slaves(fent, 0); + while (atomic_read(&fent->refcnt)) { printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n", fent->fid, atomic_read(&fent->refcnt)); @@ -109,8 +109,7 @@ struct w1_family * w1_family_registered(u8 fid) static void __w1_family_put(struct w1_family *f) { - if (atomic_dec_and_test(&f->refcnt)) - f->need_exit = 1; + atomic_dec(&f->refcnt); } void w1_family_put(struct w1_family *f) diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index ef1e1dafa19..3ca1b9298f2 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -33,6 +33,7 @@ #define W1_THERM_DS1822 0x22 #define W1_EEPROM_DS2433 0x23 #define W1_THERM_DS18B20 0x28 +#define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 #define MAXNAMELEN 32 @@ -53,7 +54,6 @@ struct w1_family struct w1_family_ops *fops; atomic_t refcnt; - u8 need_exit; }; extern spinlock_t w1_flock; @@ -63,6 +63,5 @@ void __w1_family_get(struct w1_family *); struct w1_family * w1_family_registered(u8); void w1_unregister_family(struct w1_family *); int w1_register_family(struct w1_family *); -void w1_reconnect_slaves(struct w1_family *f); #endif /* __W1_FAMILY_H */ diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 6840dfebe4d..a3a54567bfb 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -29,7 +29,11 @@ #include "w1_netlink.h" #include "w1_int.h" -static u32 w1_ids = 1; +static int w1_search_count = -1; /* Default is continual scan */ +module_param_named(search_count, w1_search_count, int, 0); + +static int w1_enable_pullup = 1; +module_param_named(enable_pullup, w1_enable_pullup, int, 0); static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, struct device_driver *driver, @@ -59,8 +63,12 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, dev->initialized = 0; dev->id = id; dev->slave_ttl = slave_ttl; - dev->search_count = -1; /* continual scan */ + dev->search_count = w1_search_count; + dev->enable_pullup = w1_enable_pullup; + /* 1 for w1_process to decrement + * 1 for __w1_remove_master_device to decrement + */ atomic_set(&dev->refcnt, 2); INIT_LIST_HEAD(&dev->slist); @@ -93,9 +101,10 @@ static void w1_free_dev(struct w1_master *dev) int w1_add_master_device(struct w1_bus_master *master) { - struct w1_master *dev; + struct w1_master *dev, *entry; int retval = 0; struct w1_netlink_msg msg; + int id, found; /* validate minimum functionality */ if (!(master->touch_bit && master->reset_bus) && @@ -104,10 +113,50 @@ 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); + /* Search for the first available id (starting at 1). */ + id = 0; + do { + ++id; + found = 0; + list_for_each_entry(entry, &w1_masters, w1_master_entry) { + if (entry->id == id) { + found = 1; + break; + } + } + } while (found); - dev = w1_alloc_dev(w1_ids++, w1_max_slave_count, w1_max_slave_ttl, &w1_master_driver, &w1_master_device); - if (!dev) + dev = w1_alloc_dev(id, w1_max_slave_count, w1_max_slave_ttl, + &w1_master_driver, &w1_master_device); + if (!dev) { + mutex_unlock(&w1_mlock); return -ENOMEM; + } + + retval = w1_create_master_attributes(dev); + if (retval) { + mutex_unlock(&w1_mlock); + goto err_out_free_dev; + } + + memcpy(dev->bus_master, master, sizeof(struct w1_bus_master)); + + dev->initialized = 1; dev->thread = kthread_run(&w1_process, dev, "%s", dev->name); if (IS_ERR(dev->thread)) { @@ -115,18 +164,10 @@ int w1_add_master_device(struct w1_bus_master *master) dev_err(&dev->dev, "Failed to create new kernel thread. err=%d\n", retval); - goto err_out_free_dev; + mutex_unlock(&w1_mlock); + goto err_out_rm_attr; } - retval = w1_create_master_attributes(dev); - if (retval) - goto err_out_kill_thread; - - memcpy(dev->bus_master, master, sizeof(struct w1_bus_master)); - - dev->initialized = 1; - - mutex_lock(&w1_mlock); list_add(&dev->w1_master_entry, &w1_masters); mutex_unlock(&w1_mlock); @@ -137,8 +178,12 @@ int w1_add_master_device(struct w1_bus_master *master) return 0; +#if 0 /* Thread cleanup code, not required currently. */ err_out_kill_thread: kthread_stop(dev->thread); +#endif +err_out_rm_attr: + w1_destroy_master_attributes(dev); err_out_free_dev: w1_free_dev(dev); @@ -148,10 +193,21 @@ err_out_free_dev: void __w1_remove_master_device(struct w1_master *dev) { struct w1_netlink_msg msg; + struct w1_slave *sl, *sln; - set_bit(W1_MASTER_NEED_EXIT, &dev->flags); kthread_stop(dev->thread); + mutex_lock(&w1_mlock); + list_del(&dev->w1_master_entry); + mutex_unlock(&w1_mlock); + + mutex_lock(&dev->mutex); + list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) + w1_slave_detach(sl); + w1_destroy_master_attributes(dev); + mutex_unlock(&dev->mutex); + atomic_dec(&dev->refcnt); + while (atomic_read(&dev->refcnt)) { dev_info(&dev->dev, "Waiting for %s to become free: refcnt=%d.\n", dev->name, atomic_read(&dev->refcnt)); diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 30b6fbf83bd..0d15b0eaf79 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -93,6 +93,40 @@ static void w1_write_bit(struct w1_master *dev, int bit) } /** + * 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) +{ + if (dev->pullup_duration && + dev->enable_pullup && dev->bus_master->set_pullup) { + dev->bus_master->set_pullup(dev->bus_master->data, + dev->pullup_duration); + } +} + +/** + * 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) +{ + if (dev->pullup_duration) { + if (dev->enable_pullup && dev->bus_master->set_pullup) + dev->bus_master->set_pullup(dev->bus_master->data, 0); + else + msleep(dev->pullup_duration); + dev->pullup_duration = 0; + } +} + +/** * Writes 8 bits. * * @param dev the master device @@ -102,11 +136,17 @@ void w1_write_8(struct w1_master *dev, u8 byte) { int i; - if (dev->bus_master->write_byte) + if (dev->bus_master->write_byte) { + w1_pre_write(dev); dev->bus_master->write_byte(dev->bus_master->data, byte); + } else - for (i = 0; i < 8; ++i) + for (i = 0; i < 8; ++i) { + if (i == 7) + w1_pre_write(dev); w1_touch_bit(dev, (byte >> i) & 0x1); + } + w1_post_write(dev); } EXPORT_SYMBOL_GPL(w1_write_8); @@ -177,7 +217,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir) * @param dev the master device * @return the byte read */ -static u8 w1_read_8(struct w1_master * dev) +u8 w1_read_8(struct w1_master *dev) { int i; u8 res = 0; @@ -190,6 +230,7 @@ static u8 w1_read_8(struct w1_master * dev) return res; } +EXPORT_SYMBOL_GPL(w1_read_8); /** * Writes a series of bytes. @@ -203,11 +244,14 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) { int i; - if (dev->bus_master->write_block) + if (dev->bus_master->write_block) { + w1_pre_write(dev); dev->bus_master->write_block(dev->bus_master->data, buf, len); + } else for (i = 0; i < len; ++i) - w1_write_8(dev, buf[i]); + w1_write_8(dev, buf[i]); /* calls w1_pre_write */ + w1_post_write(dev); } EXPORT_SYMBOL_GPL(w1_write_block); @@ -250,12 +294,24 @@ int w1_reset_bus(struct w1_master *dev) result = dev->bus_master->reset_bus(dev->bus_master->data) & 0x1; else { dev->bus_master->write_bit(dev->bus_master->data, 0); + /* minimum 480, max ? us + * be nice and sleep, except 18b20 spec lists 960us maximum, + * so until we can sleep with microsecond accuracy, spin. + * Feel free to come up with some other way to give up the + * cpu for such a short amount of time AND get it back in + * the maximum amount of time. + */ w1_delay(480); dev->bus_master->write_bit(dev->bus_master->data, 1); w1_delay(70); result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1; - w1_delay(410); + /* minmum 70 (above) + 410 = 480 us + * There aren't any timing requirements between a reset and + * the following transactions. Sleeping is safe here. + */ + /* w1_delay(410); min required time */ + msleep(1); } return result; @@ -277,7 +333,8 @@ void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_cal { dev->attempts++; if (dev->bus_master->search) - dev->bus_master->search(dev->bus_master->data, search_type, cb); + dev->bus_master->search(dev->bus_master->data, dev, + search_type, cb); else w1_search(dev, search_type, cb); } @@ -305,3 +362,20 @@ int w1_reset_select_slave(struct w1_slave *sl) return 0; } EXPORT_SYMBOL_GPL(w1_reset_select_slave); + +/** + * 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 + * write operation without a strong pullup. This is a one shot request for + * 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 + */ +void w1_next_pullup(struct w1_master *dev, int delay) +{ + dev->pullup_duration = delay; +} +EXPORT_SYMBOL_GPL(w1_next_pullup); |