Commit bab55a8a authored by Alessandro Rubini's avatar Alessandro Rubini

w1: files for new onewire implementation (not built)

This adds the files for an alternative to sockitowm. I called it "w1"
like in the Linux kernel, because "onewire" was already in use as file
name in this project.

The bus scanning code was a fun project of mine a few years ago, the
rest is what I did for my own crappy operating system in the last few
days.  This commit adds the files as modified to build in wrpc-sw, since
I have no "official" Bathos files yet to diff against.

This commit doesn't change the build, only adds the file, so actual
changes to existing code is all part of the next (smaller) commit.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent d796977e
/*
* 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 <w1.h>
/* This used to be part of dev/onewire.c, but is not onewire-specific */
/* 0 = success, -1 = error */
int8_t get_persistent_mac(uint8_t portnum, uint8_t * mac)
{
int i, class;
uint64_t rom;
for (i = 0; i < W1_MAX_DEVICES; i++) {
class = w1_class(wrpc_w1_bus.devs + i);
if (class != 0x28 && class != 0x42)
continue;
rom = wrpc_w1_bus.devs[i].rom;
mac[3] = rom >> 24;
mac[4] = rom >> 16;
mac[5] = rom >> 8;
return 0;
}
return -1;
/* FIXME: add eeprom support */
}
/* 0 = success, -1 = error */
int8_t set_persistent_mac(uint8_t portnum, uint8_t * mac)
{
/* FIXME: add eeprom support */
return -1;
}
/*
* 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 "../sockitowm/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));
}
}
}
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
/* Temperatture conviersion 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