Commit d898611f authored by Alessandro Rubini's avatar Alessandro Rubini

tools/w1-host: verbatim copies from dev/ and include/

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent e6ca8731
/*
* Eeprom support (family 0x43)
* Cesar Prados, Alessandro Rubini, 2013. GNU GPL2 or later
*/
#include <stdlib.h>
#include <string.h>
#include <wrc.h>
#include <shell.h>
#include <w1.h>
#define LSB_ADDR(X) ((X) & 0xFF)
#define MSB_ADDR(X) (((X) & 0xFF00)>>8)
static int w1_write_page(struct w1_dev *dev, int offset, const uint8_t *buffer,
int blen)
{
int i, j, es;
/* First, write scratchpad */
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDR_W_SPAD);
w1_write_byte(dev->bus, LSB_ADDR(offset));
w1_write_byte(dev->bus, MSB_ADDR(offset));
for(i = 0; i < blen; i++)
w1_write_byte(dev->bus, buffer[i]);
/* Then, read it back, and remember the return E/S */
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDR_R_SPAD);
if (w1_read_byte(dev->bus) != LSB_ADDR(offset))
return -1;
if (w1_read_byte(dev->bus) != MSB_ADDR(offset))
return -2;
es = w1_read_byte(dev->bus);
for(i = 0; i < blen; i++) {
j = w1_read_byte(dev->bus);
if (j != buffer[i])
return -3;
}
/* Finally, "copy scratchpad" to actually write */
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDR_C_SPAD);
w1_write_byte(dev->bus, LSB_ADDR(offset));
w1_write_byte(dev->bus, MSB_ADDR(offset));
w1_write_byte(dev->bus, es);
usleep(10000); /* 10ms, in theory */
/* Don't read back, as nothing useful is there (I get 0xf9, why?) */
return blen;
}
int w1_write_eeprom(struct w1_dev *dev, int offset, const uint8_t *buffer,
int blen)
{
int i, page, endpage;
int ret = 0;
/* Split the write into several page-local writes */
page = offset / 32;
endpage = (offset + blen - 1) / 32;
/* Traling part of first page */
if (offset % 32) {
if (endpage != page)
i = 32 - (offset % 32);
else
i = blen;
ret += w1_write_page(dev, offset, buffer, i);
if (ret < 0)
return ret;
buffer += i;
offset += i;
blen -= i;
}
/* Whole pages and leading part of last page */
while (blen > 0 ) {
i = blen;
if (blen > 32)
i = 32;
i = w1_write_page(dev, offset, buffer, i);
if (i < 0)
return i;
ret += i;
buffer += 32;
offset += 32;
blen -= 32;
}
return ret;
}
int w1_read_eeprom(struct w1_dev *dev, int offset, uint8_t *buffer, int blen)
{
int i;
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDR_R_MEMORY);
w1_write_byte(dev->bus, LSB_ADDR(offset));
w1_write_byte(dev->bus, MSB_ADDR(offset));
/* There is no page-size limit in reading, just go on at will */
for(i = 0; i < blen; i++)
buffer[i] = w1_read_byte(dev->bus);
return blen;
}
int w1_read_eeprom_bus(struct w1_bus *bus,
int offset, uint8_t *buffer, int blen)
{
int i, class;
for (i = 0; i < W1_MAX_DEVICES; i++) {
class = w1_class(bus->devs + i);
if (class == 0x43)
return w1_read_eeprom(bus->devs + i, offset,
buffer, blen);
}
/* not found */
return -1;
}
int w1_write_eeprom_bus(struct w1_bus *bus,
int offset, const uint8_t *buffer, int blen)
{
int i, class;
for (i = 0; i < W1_MAX_DEVICES; i++) {
class = w1_class(bus->devs + i);
if (class == 0x43)
return w1_write_eeprom(bus->devs + i, offset,
buffer, blen);
}
/* not found */
return -1;
}
#define BLEN 32
/* A shell command, for testing write: "w1w <offset> <byte> [<byte> ...]" */
static int cmd_w1_w(const char *args[])
{
int offset, i, blen;
unsigned char buf[BLEN];
if (!args[0] || !args[1])
return -1;
offset = atoi(args[0]);
for (i = 1, blen = 0; args[i] && blen < BLEN; i++, blen++) {
buf[blen] = atoi(args[i]);
pp_printf("offset %4i (0x%03x): %3i (0x%02x)\n",
offset + blen, offset + blen, buf[blen], buf[blen]);
}
i = w1_write_eeprom_bus(&wrpc_w1_bus, offset, buf, blen);
pp_printf("write(0x%x, %i): result = %i\n", offset, blen, i);
return i == blen ? 0 : -1;
}
DEFINE_WRC_COMMAND(w1w) = {
.name = "w1w",
.exec = cmd_w1_w,
};
/* A shell command, for testing read: "w1w <offset> <len> */
static int cmd_w1_r(const char *args[])
{
int offset, i, blen;
unsigned char buf[BLEN];
if (!args[0] || !args[1])
return -1;
offset = atoi(args[0]);
blen = atoi(args[1]);
if (blen > BLEN)
blen = BLEN;
i = w1_read_eeprom_bus(&wrpc_w1_bus, offset, buf, blen);
pp_printf("read(0x%x, %i): result = %i\n", offset, blen, i);
if (i <= 0 || i > blen) return -1;
for (blen = 0; blen < i; blen++) {
pp_printf("offset %4i (0x%03x): %3i (0x%02x)\n",
offset + blen, offset + blen, buf[blen], buf[blen]);
}
return i == blen ? 0 : -1;
}
DEFINE_WRC_COMMAND(w1r) = {
.name = "w1r",
.exec = cmd_w1_r,
};
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#include <wrc.h>
#include <string.h>
#include <w1.h>
#include <hw/sockit_owm_regs.h>
static inline uint32_t __wait_cycle(void *base)
{
uint32_t reg;
while ((reg = IORD_SOCKIT_OWM_CTL(base)) & SOCKIT_OWM_CTL_CYC_MSK)
;
return reg;
}
static int w1_reset(struct w1_bus *bus)
{
int portnum = bus->detail;
uint32_t reg;
IOWR_SOCKIT_OWM_CTL(BASE_ONEWIRE, (portnum << SOCKIT_OWM_CTL_SEL_OFST)
| (SOCKIT_OWM_CTL_CYC_MSK)
| (SOCKIT_OWM_CTL_RST_MSK));
reg = __wait_cycle(BASE_ONEWIRE);
/* return presence-detect pulse (1 if true) */
return (reg & SOCKIT_OWM_CTL_DAT_MSK) ? 0 : 1;
}
static int w1_read_bit(struct w1_bus *bus)
{
int portnum = bus->detail;
uint32_t reg;
IOWR_SOCKIT_OWM_CTL(BASE_ONEWIRE, (portnum << SOCKIT_OWM_CTL_SEL_OFST)
| (SOCKIT_OWM_CTL_CYC_MSK)
| (SOCKIT_OWM_CTL_DAT_MSK));
reg = __wait_cycle(BASE_ONEWIRE);
return (reg & SOCKIT_OWM_CTL_DAT_MSK) ? 1 : 0;
}
static void w1_write_bit(struct w1_bus *bus, int bit)
{
int portnum = bus->detail;
IOWR_SOCKIT_OWM_CTL(BASE_ONEWIRE, (portnum << SOCKIT_OWM_CTL_SEL_OFST)
| (SOCKIT_OWM_CTL_CYC_MSK)
| (bit ? SOCKIT_OWM_CTL_DAT_MSK : 0));
__wait_cycle(BASE_ONEWIRE);
}
struct w1_ops wrpc_w1_ops = {
.reset = w1_reset,
.read_bit = w1_read_bit,
.write_bit = w1_write_bit,
};
struct w1_bus wrpc_w1_bus;
/*
* Temperature input for DS18S20 (family 0x10)
* Alessandro Rubini, 2013 GNU GPL2 or later
*/
#include <wrc.h>
#include <w1.h>
int32_t w1_read_temp(struct w1_dev *dev, unsigned long flags)
{
static uint8_t scratchpad[8];
int class = w1_class(dev);
int32_t res;
int16_t cval;
int i;
/* The caller is expected to have checked the class. but still... */
switch(class) {
case 0x10: case 0x28: case 0x42:
break; /* Supported, at least for temperature input */
default:
return 1<<31; /* very negative */
}
/* If so asked, jump over start-conversion and only collect result */
if (flags & W1_FLAG_COLLECT)
goto collect;
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDT_CONVERT);
/* If so asked, don't wait for the conversion to be over */
if (flags & W1_FLAG_NOWAIT)
return 0;
while(wrpc_w1_ops.read_bit(dev->bus) == 0)
;
collect:
w1_match_rom(dev);
w1_write_byte(dev->bus, W1_CMDT_R_SPAD);
for (i = 0; i < sizeof(scratchpad); i++)
scratchpad[i] = w1_read_byte(dev->bus);
res = 0;
cval = scratchpad[1] << 8 | scratchpad[0];
switch(class) {
case 0x10:
/* 18S20: two bytes plus "count remain" value */
res = (int32_t)cval << 15; /* 1 decimal points */
res -= 0x4000; /* - 0.25 degrees */
res |= scratchpad[6] << 12; /* 1/16th of degree each */
break;
case 0x28:
case 0x42:
/* 18B20 and DS28EA00: only the two bytes */
res = (int32_t)cval << 12; /* 4 decimal points */
break;
}
return res;
}
int32_t w1_read_temp_bus(struct w1_bus *bus, unsigned long flags)
{
int i, class;
for (i = 0; i < W1_MAX_DEVICES; i++) {
class = w1_class(bus->devs + i);
switch(class) {
case 0x10: case 0x28: case 0x42:
return w1_read_temp(bus->devs + i, flags);
default:
break;
}
}
/* not found */
return 1 << 31;
}
/*
* Onewire generic interface
* Alessandro Rubini, 2013 GNU GPL2 or later
*/
#include <wrc.h>
#include <string.h>
#include <shell.h>
#include <w1.h>
static const struct w1_ops *ops = &wrpc_w1_ops; /* local shorter name */
void w1_write_byte(struct w1_bus *bus, int byte)
{
int i;
for (i = 1; i < 0x100; i <<= 1)
ops->write_bit(bus, byte & i ? 1 : 0);
}
int w1_read_byte(struct w1_bus *bus)
{
int i, res = 0;
for (i = 1; i < 0x100; i <<= 1)
res |= ops->read_bit(bus) ? i : 0;
usleep(100); /* inter-byte, for my eyes only */
return res;
}
/* scan_bus requires this di-bit helper */
enum __bits {B_0, B_1, B_BOTH};
/* return what we get, select it if unambiguous or the one passed */
static enum __bits __get_dibit(struct w1_bus *bus, int select)
{
int a, b;
a = ops->read_bit(bus);
b = ops->read_bit(bus);
if (a != b) {
ops->write_bit(bus, a);
return a ? B_1 : B_0;
}
ops->write_bit(bus, select);
return B_BOTH;
}
/*
* This identifies one. Returns 0 if not found, -1 on error. The current mask
* is used to return the conflicts we found: on each conflict, we follow
* what's already in our id->rom, but remember it for later scans.
*/
static int __w1_scan_one(struct w1_bus *bus, uint64_t *rom, uint64_t *cmask)
{
uint64_t mask;
int select;
enum __bits b;
if (ops->reset(bus) != 1)
return -1;
w1_write_byte(bus, 0xf0); /* search rom */
/*
* Send all bits we have (initially, zero).
* On a conflict, follow what we have in rom and possibly mark it.
*/
*cmask = 0;
for (mask = 1; mask; mask <<= 1) {
select = *rom & mask;
b = __get_dibit(bus, select);
switch(b) {
case B_1:
*rom |= mask;
case B_0:
break;
case B_BOTH:
/* if we follow 1, it's resolved, else mark it */
if (!select)
*cmask |= mask;
break;
}
}
return 0;
}
int w1_scan_bus(struct w1_bus *bus)
{
uint64_t mask;
uint64_t cmask; /* current */
struct w1_dev *d;
int i;
memset(bus->devs, 0, sizeof(bus->devs));
if (!ops->reset)
return 0; /* no devices */
for (i = 0, cmask = 0; i < W1_MAX_DEVICES; i++) {
d = bus->devs + i;
d->bus = bus;
if (i) { /* Not first: scan conflicts and resolve last */
d->rom = bus->devs[i-1].rom;
for (mask = (1ULL<<63); mask; mask >>= 1) {
/*
* Warning: lm32 compiter treats as signed!
*
* Even if mask is uint64_t, the shift in the
* for loop above is signed, so fix it.
* I prefer not to change the loop, as the
* code is in use elsewhere and I prefer to
* keep differences to a minimum
*/
if (mask & (1ULL<<62))
mask = (1ULL<<62);
if (cmask & mask)
break;
d->rom &= ~mask;
}
if (!mask) {
/* no conflicts to solve: done */
return i;
}
d->rom |= mask; /* we'll reply 1 next loop */
cmask &= ~mask;
}
if (__w1_scan_one(bus, &d->rom, &cmask)) {
/* error on this one */
return i;
}
pp_printf("W1: %08x%08x\n", (int)(d->rom >> 32), (int)d->rom);
}
return i;
}
void w1_match_rom(struct w1_dev *dev)
{
int i;
ops->reset(dev->bus);
w1_write_byte(dev->bus, W1_CMD_MATCH_ROM); /* match rom */
for (i = 0; i < 64; i+=8) {
w1_write_byte(dev->bus, (int)(dev->rom >> i) );
}
}
/* A shell command, for checking */
static int cmd_w1(const char *args[])
{
int i;
struct w1_dev *d;
int32_t temp;
w1_scan_bus(&wrpc_w1_bus);
for (i = 0; i < W1_MAX_DEVICES; i++) {
d = wrpc_w1_bus.devs + i;
if (d->rom) {
pp_printf("device %i: %08x%08x\n", i,
(int)(d->rom >> 32), (int)d->rom);
temp = w1_read_temp(d, 0);
pp_printf("temp: %d.%04d\n", temp >> 16,
(int)((temp & 0xffff) * 10 * 1000 >> 16));
}
}
return 0;
}
DEFINE_WRC_COMMAND(w1) = {
.name = "w1",
.exec = cmd_w1,
};
/*
* Onewire generic interface
* Alessandro Rubini, 2013 GNU GPL2 or later
*/
#ifndef __BATHOS_W1_H__
#define __BATHOS_W1_H__
#include <stdint.h>
#define W1_MAX_DEVICES 8 /* we have no alloc */
struct w1_dev {
struct w1_bus *bus;
uint64_t rom;
};
static inline int w1_class(struct w1_dev *dev)
{
return dev->rom & 0xff;
}
struct w1_bus {
unsigned long detail; /* gpio bit or whatever (driver-specific) */
struct w1_dev devs[W1_MAX_DEVICES];
};
/*
* The low-level driver is based on this set of operations. We expect to
* only have one set of such operations in each build. (i.e., no bus-specific
* operations, to keep the thing simple and small).
*/
struct w1_ops {
int (*reset)(struct w1_bus *bus); /* returns 1 on "present" */
int (*read_bit)(struct w1_bus *bus);
void (*write_bit)(struct w1_bus *bus, int bit);
};
/* Library functions */
extern int w1_scan_bus(struct w1_bus *bus);
extern void w1_write_byte(struct w1_bus *bus, int byte);
extern int w1_read_byte(struct w1_bus *bus);
extern void w1_match_rom(struct w1_dev *dev);
#define W1_CMD_SEARCH_ROM 0xf0
#define W1_CMD_READ_ROM 0x33
#define W1_CMD_MATCH_ROM 0x55
#define W1_CMD_SKIP_ROM 0xcc
#define W1_CMD_ASEARCH 0xec
/* commands for specific families */
#define W1_CMDT_CONVERT 0x44
#define W1_CMDT_W_SPAD 0x4e
#define W1_CMDT_R_SPAD 0xbe
#define W1_CMDT_CP_SPAD 0x48
#define W1_CMDT_RECALL 0xb8
#define W1_CMDT_R_PS 0xb4
/* EEPROM DS28EC20 */
#define W1_CMDR_W_SPAD 0x0f
#define W1_CMDR_R_SPAD 0xaa
#define W1_CMDR_C_SPAD 0x55
#define W1_CMDR_R_MEMORY 0xf0
#define W1_CMDR_EXT_R_MEMORY 0xa5
/* Temperature conversion takes time: by default wait, but allow flags */
#define W1_FLAG_NOWAIT 0x01 /* start conversion only*/
#define W1_FLAG_COLLECT 0x02 /* don't start, just get output */
/* These functions are dev-specific */
extern int32_t w1_read_temp(struct w1_dev *dev, unsigned long flags);
extern int w1_read_eeprom(struct w1_dev *dev,
int offset, uint8_t *buffer, int blen);
extern int w1_write_eeprom(struct w1_dev *dev,
int offset, const uint8_t *buffer, int blen);
/* These are generic, using the first suitable device in the bus */
extern int32_t w1_read_temp_bus(struct w1_bus *bus, unsigned long flags);
extern int w1_read_eeprom_bus(struct w1_bus *bus,
int offset, uint8_t *buffer, int blen);
extern int w1_write_eeprom_bus(struct w1_bus *bus,
int offset, const uint8_t *buffer, int blen);
extern struct w1_ops wrpc_w1_ops;
extern struct w1_bus wrpc_w1_bus;
#endif /* __BATHOS_W1_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment