Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 1-wire bus master configuration
#
menu "1-wire Bus Masters"
config W1_MASTER_AMD_AXI
tristate "AMD AXI 1-wire bus host"
help
Say Y here is you want to support the AMD AXI 1-wire IP core.
This driver makes use of the programmable logic IP to perform
correctly timed 1 wire transactions without relying on GPIO timing
through the kernel.
This driver can also be built as a module. If so, the module will be
called amd_w1_axi.
config W1_MASTER_MATROX
tristate "Matrox G400 transport layer for 1-wire"
depends on PCI
help
Say Y here if you want to communicate with your 1-wire devices
using Matrox's G400 GPIO pins.
This support is also available as a module. If so, the module
will be called matrox_w1.
config W1_MASTER_DS2490
tristate "DS2490 USB <-> W1 transport layer for 1-wire"
depends on USB
help
Say Y here if you want to have a driver for DS2490 based USB <-> W1 bridges,
for example DS9490*.
This support is also available as a module. If so, the module
will be called ds2490.
config W1_MASTER_DS2482
tristate "Maxim DS2482 I2C to 1-Wire bridge"
depends on I2C
help
If you say yes here you get support for the Maxim DS2482
I2C to 1-Wire bridge.
This driver can also be built as a module. If so, the module
will be called ds2482.
config W1_MASTER_MXC
tristate "Freescale MXC 1-wire busmaster"
depends on ARCH_MXC || COMPILE_TEST
help
Say Y here to enable MXC 1-wire host
config W1_MASTER_GPIO
tristate "GPIO 1-wire busmaster"
depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you want to communicate with your 1-wire devices using
GPIO pins. This driver uses the GPIO API to control the wire.
This support is also available as a module. If so, the module
will be called w1-gpio.
config HDQ_MASTER_OMAP
tristate "OMAP HDQ driver"
depends on ARCH_OMAP || COMPILE_TEST
help
Say Y here if you want support for the 1-wire or HDQ Interface
on an OMAP processor.
config W1_MASTER_SGI
tristate "SGI ASIC driver"
help
Say Y here if you want support for your 1-wire devices using
SGI ASIC 1-Wire interface
This support is also available as a module. If so, the module
will be called sgi_w1.
config W1_MASTER_UART
tristate "UART 1-wire driver"
depends on SERIAL_DEV_BUS
help
Say Y here if you want to communicate with your 1-wire devices using
UART interface.
This support is also available as a module. If so, the module
will be called w1-uart.
endmenu
+15
View File
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for 1-wire bus master drivers.
#
obj-$(CONFIG_W1_MASTER_AMD_AXI) += amd_axi_w1.o
obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o
obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o
obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o
obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o
obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o
obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o
obj-$(CONFIG_W1_MASTER_SGI) += sgi_w1.o
obj-$(CONFIG_W1_MASTER_UART) += w1-uart.o
+395
View File
@@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* amd_axi_w1 - AMD 1Wire programmable logic bus host driver
*
* Copyright (C) 2022-2023 Advanced Micro Devices, Inc. All Rights Reserved.
*/
#include <linux/atomic.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/w1.h>
/* 1-wire AMD IP definition */
#define AXIW1_IPID 0x10ee4453
/* Registers offset */
#define AXIW1_INST_REG 0x0
#define AXIW1_CTRL_REG 0x4
#define AXIW1_IRQE_REG 0x8
#define AXIW1_STAT_REG 0xC
#define AXIW1_DATA_REG 0x10
#define AXIW1_IPVER_REG 0x18
#define AXIW1_IPID_REG 0x1C
/* Instructions */
#define AXIW1_INITPRES 0x0800
#define AXIW1_READBIT 0x0C00
#define AXIW1_WRITEBIT 0x0E00
#define AXIW1_READBYTE 0x0D00
#define AXIW1_WRITEBYTE 0x0F00
/* Status flag masks */
#define AXIW1_DONE BIT(0)
#define AXIW1_READY BIT(4)
#define AXIW1_PRESENCE BIT(31)
#define AXIW1_MAJORVER_MASK GENMASK(23, 8)
#define AXIW1_MINORVER_MASK GENMASK(7, 0)
/* Control flag */
#define AXIW1_GO BIT(0)
#define AXI_CLEAR 0
#define AXI_RESET BIT(31)
#define AXIW1_READDATA BIT(0)
/* Interrupt Enable */
#define AXIW1_READY_IRQ_EN BIT(4)
#define AXIW1_DONE_IRQ_EN BIT(0)
#define AXIW1_TIMEOUT msecs_to_jiffies(100)
#define DRIVER_NAME "amd_axi_w1"
struct amd_axi_w1_local {
struct device *dev;
void __iomem *base_addr;
int irq;
atomic_t flag; /* Set on IRQ, cleared once serviced */
wait_queue_head_t wait_queue;
struct w1_bus_master bus_host;
};
/**
* amd_axi_w1_wait_irq_interruptible_timeout() - Wait for IRQ with timeout.
*
* @amd_axi_w1_local: Pointer to device structure
* @IRQ: IRQ channel to wait on
*
* Return: %0 - OK, %-EINTR - Interrupted, %-EBUSY - Timed out
*/
static int amd_axi_w1_wait_irq_interruptible_timeout(struct amd_axi_w1_local *amd_axi_w1_local,
u32 IRQ)
{
int ret;
/* Enable the IRQ requested and wait for flag to indicate it's been triggered */
iowrite32(IRQ, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG);
ret = wait_event_interruptible_timeout(amd_axi_w1_local->wait_queue,
atomic_read(&amd_axi_w1_local->flag) != 0,
AXIW1_TIMEOUT);
if (ret < 0) {
dev_err(amd_axi_w1_local->dev, "Wait IRQ Interrupted\n");
return -EINTR;
}
if (!ret) {
dev_err(amd_axi_w1_local->dev, "Wait IRQ Timeout\n");
return -EBUSY;
}
atomic_set(&amd_axi_w1_local->flag, 0);
return 0;
}
/**
* amd_axi_w1_touch_bit() - Performs the touch-bit function - write a 0 or 1 and reads the level.
*
* @data: Pointer to device structure
* @bit: The level to write
*
* Return: The level read
*/
static u8 amd_axi_w1_touch_bit(void *data, u8 bit)
{
struct amd_axi_w1_local *amd_axi_w1_local = data;
u8 val = 0;
int rc;
/* Wait for READY signal to be 1 to ensure 1-wire IP is ready */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local,
AXIW1_READY_IRQ_EN);
if (rc < 0)
return 1; /* Callee doesn't test for error. Return inactive bus state */
}
if (bit)
/* Read. Write read Bit command in register 0 */
iowrite32(AXIW1_READBIT, amd_axi_w1_local->base_addr + AXIW1_INST_REG);
else
/* Write. Write tx Bit command in instruction register with bit to transmit */
iowrite32(AXIW1_WRITEBIT + (bit & 0x01),
amd_axi_w1_local->base_addr + AXIW1_INST_REG);
/* Write Go signal and clear control reset signal in control register */
iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
/* Wait for done signal to be 1 */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN);
if (rc < 0)
return 1; /* Callee doesn't test for error. Return inactive bus state */
}
/* If read, Retrieve data from register */
if (bit)
val = (u8)(ioread32(amd_axi_w1_local->base_addr + AXIW1_DATA_REG) & AXIW1_READDATA);
/* Clear Go signal in register 1 */
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
return val;
}
/**
* amd_axi_w1_read_byte - Performs the read byte function.
*
* @data: Pointer to device structure
* Return: The value read
*/
static u8 amd_axi_w1_read_byte(void *data)
{
struct amd_axi_w1_local *amd_axi_w1_local = data;
u8 val = 0;
int rc;
/* Wait for READY signal to be 1 to ensure 1-wire IP is ready */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local,
AXIW1_READY_IRQ_EN);
if (rc < 0)
return 0xFF; /* Return inactive bus state */
}
/* Write read Byte command in instruction register*/
iowrite32(AXIW1_READBYTE, amd_axi_w1_local->base_addr + AXIW1_INST_REG);
/* Write Go signal and clear control reset signal in control register */
iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
/* Wait for done signal to be 1 */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN);
if (rc < 0)
return 0xFF; /* Return inactive bus state */
}
/* Retrieve LSB bit in data register to get RX byte */
val = (u8)(ioread32(amd_axi_w1_local->base_addr + AXIW1_DATA_REG) & 0x000000FF);
/* Clear Go signal in control register */
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
return val;
}
/**
* amd_axi_w1_write_byte - Performs the write byte function.
*
* @data: The ds2482 channel pointer
* @val: The value to write
*/
static void amd_axi_w1_write_byte(void *data, u8 val)
{
struct amd_axi_w1_local *amd_axi_w1_local = data;
int rc;
/* Wait for READY signal to be 1 to ensure 1-wire IP is ready */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local,
AXIW1_READY_IRQ_EN);
if (rc < 0)
return;
}
/* Write tx Byte command in instruction register with bit to transmit */
iowrite32(AXIW1_WRITEBYTE + val, amd_axi_w1_local->base_addr + AXIW1_INST_REG);
/* Write Go signal and clear control reset signal in register 1 */
iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
/* Wait for done signal to be 1 */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local,
AXIW1_DONE_IRQ_EN);
if (rc < 0)
return;
}
/* Clear Go signal in control register */
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
}
/**
* amd_axi_w1_reset_bus() - Issues a reset bus sequence.
*
* @data: the bus host data struct
* Return: 0=Device present, 1=No device present or error
*/
static u8 amd_axi_w1_reset_bus(void *data)
{
struct amd_axi_w1_local *amd_axi_w1_local = data;
u8 val = 0;
int rc;
/* Reset 1-wire Axi IP */
iowrite32(AXI_RESET, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
/* Wait for READY signal to be 1 to ensure 1-wire IP is ready */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local,
AXIW1_READY_IRQ_EN);
if (rc < 0)
return 1; /* Something went wrong with the hardware */
}
/* Write Initialization command in instruction register */
iowrite32(AXIW1_INITPRES, amd_axi_w1_local->base_addr + AXIW1_INST_REG);
/* Write Go signal and clear control reset signal in register 1 */
iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
/* Wait for done signal to be 1 */
while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) {
rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN);
if (rc < 0)
return 1; /* Something went wrong with the hardware */
}
/* Retrieve MSB bit in status register to get failure bit */
if ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_PRESENCE) != 0)
val = 1;
/* Clear Go signal in control register */
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
return val;
}
/* Reset the 1-wire AXI IP. Put the IP in reset state and clear registers */
static void amd_axi_w1_reset(struct amd_axi_w1_local *amd_axi_w1_local)
{
iowrite32(AXI_RESET, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG);
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_INST_REG);
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG);
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_STAT_REG);
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_DATA_REG);
}
static irqreturn_t amd_axi_w1_irq(int irq, void *lp)
{
struct amd_axi_w1_local *amd_axi_w1_local = lp;
/* Reset interrupt trigger */
iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG);
atomic_set(&amd_axi_w1_local->flag, 1);
wake_up_interruptible(&amd_axi_w1_local->wait_queue);
return IRQ_HANDLED;
}
static int amd_axi_w1_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct amd_axi_w1_local *lp;
struct clk *clk;
u32 ver_major, ver_minor;
int val, rc = 0;
lp = devm_kzalloc(dev, sizeof(*lp), GFP_KERNEL);
if (!lp)
return -ENOMEM;
lp->dev = dev;
lp->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lp->base_addr))
return PTR_ERR(lp->base_addr);
lp->irq = platform_get_irq(pdev, 0);
if (lp->irq < 0)
return lp->irq;
rc = devm_request_irq(dev, lp->irq, &amd_axi_w1_irq, IRQF_TRIGGER_HIGH, DRIVER_NAME, lp);
if (rc)
return rc;
/* Initialize wait queue and flag */
init_waitqueue_head(&lp->wait_queue);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
/* Verify IP presence in HW */
if (ioread32(lp->base_addr + AXIW1_IPID_REG) != AXIW1_IPID) {
dev_err(dev, "AMD 1-wire IP not detected in hardware\n");
return -ENODEV;
}
/*
* Allow for future driver expansion supporting new hardware features
* This driver currently only supports hardware 1.x, but include logic
* to detect if a potentially incompatible future version is used
* by reading major version ID. It is highly undesirable for new IP versions
* to break the API, but this code will at least allow for graceful failure
* should that happen. Future new features can be enabled by hardware
* incrementing the minor version and augmenting the driver to detect capability
* using the minor version number
*/
val = ioread32(lp->base_addr + AXIW1_IPVER_REG);
ver_major = FIELD_GET(AXIW1_MAJORVER_MASK, val);
ver_minor = FIELD_GET(AXIW1_MINORVER_MASK, val);
if (ver_major != 1) {
dev_err(dev, "AMD AXI W1 host version %u.%u is not supported by this driver",
ver_major, ver_minor);
return -ENODEV;
}
lp->bus_host.data = lp;
lp->bus_host.touch_bit = amd_axi_w1_touch_bit;
lp->bus_host.read_byte = amd_axi_w1_read_byte;
lp->bus_host.write_byte = amd_axi_w1_write_byte;
lp->bus_host.reset_bus = amd_axi_w1_reset_bus;
amd_axi_w1_reset(lp);
platform_set_drvdata(pdev, lp);
rc = w1_add_master_device(&lp->bus_host);
if (rc) {
dev_err(dev, "Could not add host device\n");
return rc;
}
return 0;
}
static void amd_axi_w1_remove(struct platform_device *pdev)
{
struct amd_axi_w1_local *lp = platform_get_drvdata(pdev);
w1_remove_master_device(&lp->bus_host);
}
static const struct of_device_id amd_axi_w1_of_match[] = {
{ .compatible = "amd,axi-1wire-host" },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, amd_axi_w1_of_match);
static struct platform_driver amd_axi_w1_driver = {
.probe = amd_axi_w1_probe,
.remove = amd_axi_w1_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = amd_axi_w1_of_match,
},
};
module_platform_driver(amd_axi_w1_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kris Chaplin <kris.chaplin@amd.com>");
MODULE_DESCRIPTION("Driver for AMD AXI 1 Wire IP core");
+561
View File
@@ -0,0 +1,561 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ds2482.c - provides i2c to w1-master bridge(s)
* Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
*
* The DS2482 is a sensor chip made by Dallas Semiconductor (Maxim).
* It is a I2C to 1-wire bridge.
* There are two variations: -100 and -800, which have 1 or 8 1-wire ports.
* The complete datasheet can be obtained from MAXIM's website at:
* https://www.analog.com/en/products/ds2482-100.html
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/w1.h>
/*
* Allow the active pullup to be disabled, default is enabled.
*
* Note from the DS2482 datasheet:
* The APU bit controls whether an active pullup (controlled slew-rate
* transistor) or a passive pullup (Rwpu resistor) will be used to drive
* a 1-Wire line from low to high. When APU = 0, active pullup is disabled
* (resistor mode). Active Pullup should always be selected unless there is
* only a single slave on the 1-Wire line.
*/
static int ds2482_active_pullup = 1;
module_param_named(active_pullup, ds2482_active_pullup, int, 0644);
MODULE_PARM_DESC(active_pullup, "Active pullup (apply to all buses): " \
"0-disable, 1-enable (default)");
/* extra configurations - e.g. 1WS */
static int extra_config;
module_param(extra_config, int, 0644);
MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8=1WS");
/*
* The DS2482 registers - there are 3 registers that are addressed by a read
* pointer. The read pointer is set by the last command executed.
*
* To read the data, issue a register read for any address
*/
#define DS2482_CMD_RESET 0xF0 /* No param */
#define DS2482_CMD_SET_READ_PTR 0xE1 /* Param: DS2482_PTR_CODE_xxx */
#define DS2482_CMD_CHANNEL_SELECT 0xC3 /* Param: Channel byte - DS2482-800 only */
#define DS2482_CMD_WRITE_CONFIG 0xD2 /* Param: Config byte */
#define DS2482_CMD_1WIRE_RESET 0xB4 /* Param: None */
#define DS2482_CMD_1WIRE_SINGLE_BIT 0x87 /* Param: Bit byte (bit7) */
#define DS2482_CMD_1WIRE_WRITE_BYTE 0xA5 /* Param: Data byte */
#define DS2482_CMD_1WIRE_READ_BYTE 0x96 /* Param: None */
/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */
#define DS2482_CMD_1WIRE_TRIPLET 0x78 /* Param: Dir byte (bit7) */
/* Values for DS2482_CMD_SET_READ_PTR */
#define DS2482_PTR_CODE_STATUS 0xF0
#define DS2482_PTR_CODE_DATA 0xE1
#define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */
#define DS2482_PTR_CODE_CONFIG 0xC3
/*
* Configure Register bit definitions
* The top 4 bits always read 0.
* To write, the top nibble must be the 1's compl. of the low nibble.
*/
#define DS2482_REG_CFG_1WS 0x08 /* 1-wire speed */
#define DS2482_REG_CFG_SPU 0x04 /* strong pull-up */
#define DS2482_REG_CFG_PPM 0x02 /* presence pulse masking */
#define DS2482_REG_CFG_APU 0x01 /* active pull-up */
/*
* Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only).
* To set the channel, write the value at the index of the channel.
* Read and compare against the corresponding value to verify the change.
*/
static const u8 ds2482_chan_wr[8] = { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 };
static const u8 ds2482_chan_rd[8] = { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 };
/*
* Status Register bit definitions (read only)
*/
#define DS2482_REG_STS_DIR 0x80
#define DS2482_REG_STS_TSB 0x40
#define DS2482_REG_STS_SBR 0x20
#define DS2482_REG_STS_RST 0x10
#define DS2482_REG_STS_LL 0x08
#define DS2482_REG_STS_SD 0x04
#define DS2482_REG_STS_PPD 0x02
#define DS2482_REG_STS_1WB 0x01
/*
* Client data (each client gets its own)
*/
struct ds2482_data;
struct ds2482_w1_chan {
struct ds2482_data *pdev;
u8 channel;
struct w1_bus_master w1_bm;
};
struct ds2482_data {
struct i2c_client *client;
struct mutex access_lock;
/* 1-wire interface(s) */
int w1_count; /* 1 or 8 */
struct ds2482_w1_chan w1_ch[8];
/* per-device values */
u8 channel;
u8 read_prt; /* see DS2482_PTR_CODE_xxx */
u8 reg_config;
};
/**
* ds2482_calculate_config - Helper to calculate values for configuration register
* @conf: the raw config value
* Return: the value w/ complements that can be written to register
*/
static inline u8 ds2482_calculate_config(u8 conf)
{
conf |= extra_config;
if (ds2482_active_pullup)
conf |= DS2482_REG_CFG_APU;
return conf | ((~conf & 0x0f) << 4);
}
/**
* ds2482_select_register - Sets the read pointer.
* @pdev: The ds2482 client pointer
* @read_ptr: see DS2482_PTR_CODE_xxx above
* Return: -1 on failure, 0 on success
*/
static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
{
if (pdev->read_prt != read_ptr) {
if (i2c_smbus_write_byte_data(pdev->client,
DS2482_CMD_SET_READ_PTR,
read_ptr) < 0)
return -1;
pdev->read_prt = read_ptr;
}
return 0;
}
/**
* ds2482_send_cmd - Sends a command without a parameter
* @pdev: The ds2482 client pointer
* @cmd: DS2482_CMD_RESET,
* DS2482_CMD_1WIRE_RESET,
* DS2482_CMD_1WIRE_READ_BYTE
* Return: -1 on failure, 0 on success
*/
static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
{
if (i2c_smbus_write_byte(pdev->client, cmd) < 0)
return -1;
pdev->read_prt = DS2482_PTR_CODE_STATUS;
return 0;
}
/**
* ds2482_send_cmd_data - Sends a command with a parameter
* @pdev: The ds2482 client pointer
* @cmd: DS2482_CMD_WRITE_CONFIG,
* DS2482_CMD_1WIRE_SINGLE_BIT,
* DS2482_CMD_1WIRE_WRITE_BYTE,
* DS2482_CMD_1WIRE_TRIPLET
* @byte: The data to send
* Return: -1 on failure, 0 on success
*/
static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
u8 cmd, u8 byte)
{
if (i2c_smbus_write_byte_data(pdev->client, cmd, byte) < 0)
return -1;
/* all cmds leave in STATUS, except CONFIG */
pdev->read_prt = (cmd != DS2482_CMD_WRITE_CONFIG) ?
DS2482_PTR_CODE_STATUS : DS2482_PTR_CODE_CONFIG;
return 0;
}
/*
* 1-Wire interface code
*/
#define DS2482_WAIT_IDLE_TIMEOUT 100
/**
* ds2482_wait_1wire_idle - Waits until the 1-wire interface is idle (not busy)
*
* @pdev: Pointer to the device structure
* Return: the last value read from status or -1 (failure)
*/
static int ds2482_wait_1wire_idle(struct ds2482_data *pdev)
{
int temp = -1;
int retries = 0;
if (!ds2482_select_register(pdev, DS2482_PTR_CODE_STATUS)) {
do {
temp = i2c_smbus_read_byte(pdev->client);
} while ((temp >= 0) && (temp & DS2482_REG_STS_1WB) &&
(++retries < DS2482_WAIT_IDLE_TIMEOUT));
}
if (retries >= DS2482_WAIT_IDLE_TIMEOUT)
pr_err("%s: timeout on channel %d\n",
__func__, pdev->channel);
return temp;
}
/**
* ds2482_set_channel - Selects a w1 channel.
* The 1-wire interface must be idle before calling this function.
*
* @pdev: The ds2482 client pointer
* @channel: 0-7
* Return: -1 (failure) or 0 (success)
*/
static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
{
if (i2c_smbus_write_byte_data(pdev->client, DS2482_CMD_CHANNEL_SELECT,
ds2482_chan_wr[channel]) < 0)
return -1;
pdev->read_prt = DS2482_PTR_CODE_CHANNEL;
pdev->channel = -1;
if (i2c_smbus_read_byte(pdev->client) == ds2482_chan_rd[channel]) {
pdev->channel = channel;
return 0;
}
return -1;
}
/**
* ds2482_w1_touch_bit - Performs the touch-bit function, which writes a 0 or 1 and reads the level.
*
* @data: The ds2482 channel pointer
* @bit: The level to write: 0 or non-zero
* Return: The level read: 0 or 1
*/
static u8 ds2482_w1_touch_bit(void *data, u8 bit)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
int status = -1;
mutex_lock(&pdev->access_lock);
/* Select the channel */
ds2482_wait_1wire_idle(pdev);
if (pdev->w1_count > 1)
ds2482_set_channel(pdev, pchan->channel);
/* Send the touch command, wait until 1WB == 0, return the status */
if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_SINGLE_BIT,
bit ? 0xFF : 0))
status = ds2482_wait_1wire_idle(pdev);
mutex_unlock(&pdev->access_lock);
return (status & DS2482_REG_STS_SBR) ? 1 : 0;
}
/**
* ds2482_w1_triplet - Performs the triplet function, which reads two bits and writes a bit.
* The bit written is determined by the two reads:
* 00 => dbit, 01 => 0, 10 => 1
*
* @data: The ds2482 channel pointer
* @dbit: The direction to choose if both branches are valid
* Return: b0=read1 b1=read2 b3=bit written
*/
static u8 ds2482_w1_triplet(void *data, u8 dbit)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
int status = (3 << 5);
mutex_lock(&pdev->access_lock);
/* Select the channel */
ds2482_wait_1wire_idle(pdev);
if (pdev->w1_count > 1)
ds2482_set_channel(pdev, pchan->channel);
/* Send the triplet command, wait until 1WB == 0, return the status */
if (!ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_TRIPLET,
dbit ? 0xFF : 0))
status = ds2482_wait_1wire_idle(pdev);
mutex_unlock(&pdev->access_lock);
/* Decode the status */
return (status >> 5);
}
/**
* ds2482_w1_write_byte - Performs the write byte function.
*
* @data: The ds2482 channel pointer
* @byte: The value to write
*/
static void ds2482_w1_write_byte(void *data, u8 byte)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
mutex_lock(&pdev->access_lock);
/* Select the channel */
ds2482_wait_1wire_idle(pdev);
if (pdev->w1_count > 1)
ds2482_set_channel(pdev, pchan->channel);
/* Send the write byte command */
ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_WRITE_BYTE, byte);
mutex_unlock(&pdev->access_lock);
}
/**
* ds2482_w1_read_byte - Performs the read byte function.
*
* @data: The ds2482 channel pointer
* Return: The value read
*/
static u8 ds2482_w1_read_byte(void *data)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
int result;
mutex_lock(&pdev->access_lock);
/* Select the channel */
ds2482_wait_1wire_idle(pdev);
if (pdev->w1_count > 1)
ds2482_set_channel(pdev, pchan->channel);
/* Send the read byte command */
ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_READ_BYTE);
/* Wait until 1WB == 0 */
ds2482_wait_1wire_idle(pdev);
/* Select the data register */
ds2482_select_register(pdev, DS2482_PTR_CODE_DATA);
/* Read the data byte */
result = i2c_smbus_read_byte(pdev->client);
mutex_unlock(&pdev->access_lock);
return result;
}
/**
* ds2482_w1_reset_bus - Sends a reset on the 1-wire interface
*
* @data: The ds2482 channel pointer
* Return: 0=Device present, 1=No device present or error
*/
static u8 ds2482_w1_reset_bus(void *data)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
int err;
u8 retval = 1;
mutex_lock(&pdev->access_lock);
/* Select the channel */
ds2482_wait_1wire_idle(pdev);
if (pdev->w1_count > 1)
ds2482_set_channel(pdev, pchan->channel);
/* Send the reset command */
err = ds2482_send_cmd(pdev, DS2482_CMD_1WIRE_RESET);
if (err >= 0) {
/* Wait until the reset is complete */
err = ds2482_wait_1wire_idle(pdev);
retval = !(err & DS2482_REG_STS_PPD);
/* If the chip did reset since detect, re-config it */
if (err & DS2482_REG_STS_RST)
ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG,
ds2482_calculate_config(0x00));
}
mutex_unlock(&pdev->access_lock);
return retval;
}
static u8 ds2482_w1_set_pullup(void *data, int delay)
{
struct ds2482_w1_chan *pchan = data;
struct ds2482_data *pdev = pchan->pdev;
u8 retval = 1;
/* if delay is non-zero activate the pullup,
* the strong pullup will be automatically deactivated
* by the master, so do not explicitly deactive it
*/
if (delay) {
/* both waits are crucial, otherwise devices might not be
* powered long enough, causing e.g. a w1_therm sensor to
* provide wrong conversion results
*/
ds2482_wait_1wire_idle(pdev);
/* note: it seems like both SPU and APU have to be set! */
retval = ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG,
ds2482_calculate_config(DS2482_REG_CFG_SPU |
DS2482_REG_CFG_APU));
ds2482_wait_1wire_idle(pdev);
}
return retval;
}
static int ds2482_probe(struct i2c_client *client)
{
struct ds2482_data *data;
int err = -ENODEV;
int temp1;
int idx;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
data = devm_kzalloc(&client->dev, sizeof(struct ds2482_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
ret = devm_regulator_get_enable(&client->dev, "vcc");
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to enable regulator\n");
data->client = client;
i2c_set_clientdata(client, data);
/* Reset the device (sets the read_ptr to status) */
if (ds2482_send_cmd(data, DS2482_CMD_RESET) < 0) {
dev_warn(&client->dev, "DS2482 reset failed.\n");
return err;
}
/* Sleep at least 525ns to allow the reset to complete */
ndelay(525);
/* Read the status byte - only reset bit and line should be set */
temp1 = i2c_smbus_read_byte(client);
if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) {
dev_warn(&client->dev, "DS2482 reset status "
"0x%02X - not a DS2482\n", temp1);
return err;
}
/* Detect the 8-port version */
data->w1_count = 1;
if (ds2482_set_channel(data, 7) == 0)
data->w1_count = 8;
/* Set all config items to 0 (off) */
ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG,
ds2482_calculate_config(0x00));
mutex_init(&data->access_lock);
/* Register 1-wire interface(s) */
for (idx = 0; idx < data->w1_count; idx++) {
data->w1_ch[idx].pdev = data;
data->w1_ch[idx].channel = idx;
/* Populate all the w1 bus master stuff */
data->w1_ch[idx].w1_bm.data = &data->w1_ch[idx];
data->w1_ch[idx].w1_bm.read_byte = ds2482_w1_read_byte;
data->w1_ch[idx].w1_bm.write_byte = ds2482_w1_write_byte;
data->w1_ch[idx].w1_bm.touch_bit = ds2482_w1_touch_bit;
data->w1_ch[idx].w1_bm.triplet = ds2482_w1_triplet;
data->w1_ch[idx].w1_bm.reset_bus = ds2482_w1_reset_bus;
data->w1_ch[idx].w1_bm.set_pullup = ds2482_w1_set_pullup;
err = w1_add_master_device(&data->w1_ch[idx].w1_bm);
if (err) {
data->w1_ch[idx].pdev = NULL;
goto exit_w1_remove;
}
}
return 0;
exit_w1_remove:
for (idx = 0; idx < data->w1_count; idx++) {
if (data->w1_ch[idx].pdev != NULL)
w1_remove_master_device(&data->w1_ch[idx].w1_bm);
}
return err;
}
static void ds2482_remove(struct i2c_client *client)
{
struct ds2482_data *data = i2c_get_clientdata(client);
int idx;
/* Unregister the 1-wire bridge(s) */
for (idx = 0; idx < data->w1_count; idx++) {
if (data->w1_ch[idx].pdev != NULL)
w1_remove_master_device(&data->w1_ch[idx].w1_bm);
}
}
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id ds2482_id[] = {
{ .name = "ds2482" },
{ .name = "ds2484" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds2482_id);
static struct i2c_driver ds2482_driver = {
.driver = {
.name = "ds2482",
},
.probe = ds2482_probe,
.remove = ds2482_remove,
.id_table = ds2482_id,
};
module_i2c_driver(ds2482_driver);
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("DS2482 driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* matrox_w1.c
*
* Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
*/
#include <asm/types.h>
#include <linux/atomic.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#include <linux/pci.h>
#include <linux/w1.h>
/*
* Matrox G400 DDC registers.
*/
#define MATROX_G400_DDC_CLK (1<<4)
#define MATROX_G400_DDC_DATA (1<<1)
#define MATROX_BASE 0x3C00
#define MATROX_STATUS 0x1e14
#define MATROX_PORT_INDEX_OFFSET 0x00
#define MATROX_PORT_DATA_OFFSET 0x0A
#define MATROX_GET_CONTROL 0x2A
#define MATROX_GET_DATA 0x2B
#define MATROX_CURSOR_CTL 0x06
struct matrox_device {
void __iomem *base_addr;
void __iomem *port_index;
void __iomem *port_data;
u8 data_mask;
unsigned long phys_addr;
void __iomem *virt_addr;
struct w1_bus_master *bus_master;
};
/*
* These functions read and write DDC Data bit.
*
* Using tristate pins, since i can't find any open-drain pin in whole motherboard.
* Unfortunately we can't connect to Intel's 82801xx IO controller
* since we don't know motherboard schema, which has pretty unused(may be not) GPIO.
*
* I've heard that PIIX also has open drain pin.
*
* Port mapping.
*/
static inline u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg)
{
u8 ret;
writeb(reg, dev->port_index);
ret = readb(dev->port_data);
barrier();
return ret;
}
static inline void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val)
{
writeb(reg, dev->port_index);
writeb(val, dev->port_data);
wmb();
}
static void matrox_w1_write_ddc_bit(void *data, u8 bit)
{
u8 ret;
struct matrox_device *dev = data;
if (bit)
bit = 0;
else
bit = dev->data_mask;
ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL);
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit));
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00);
}
static u8 matrox_w1_read_ddc_bit(void *data)
{
u8 ret;
struct matrox_device *dev = data;
ret = matrox_w1_read_reg(dev, MATROX_GET_DATA);
return ret;
}
static void matrox_w1_hw_init(struct matrox_device *dev)
{
matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF);
matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
}
static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct matrox_device *dev;
int err;
if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400)
return -ENODEV;
dev = kzalloc(sizeof(struct matrox_device) +
sizeof(struct w1_bus_master), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->bus_master = (struct w1_bus_master *)(dev + 1);
/*
* True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c
*/
dev->phys_addr = pci_resource_start(pdev, 1);
dev->virt_addr = ioremap(dev->phys_addr, 16384);
if (!dev->virt_addr) {
dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n",
__func__, dev->phys_addr, 16384);
err = -EIO;
goto err_out_free_device;
}
dev->base_addr = dev->virt_addr + MATROX_BASE;
dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET;
dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET;
dev->data_mask = (MATROX_G400_DDC_DATA);
matrox_w1_hw_init(dev);
dev->bus_master->data = dev;
dev->bus_master->read_bit = &matrox_w1_read_ddc_bit;
dev->bus_master->write_bit = &matrox_w1_write_ddc_bit;
err = w1_add_master_device(dev->bus_master);
if (err)
goto err_out_free_device;
pci_set_drvdata(pdev, dev);
dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n");
return 0;
err_out_free_device:
if (dev->virt_addr)
iounmap(dev->virt_addr);
kfree(dev);
return err;
}
static void matrox_w1_remove(struct pci_dev *pdev)
{
struct matrox_device *dev = pci_get_drvdata(pdev);
w1_remove_master_device(dev->bus_master);
iounmap(dev->virt_addr);
kfree(dev);
}
static struct pci_device_id matrox_w1_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) },
{ },
};
MODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
static struct pci_driver matrox_w1_pci_driver = {
.name = "matrox_w1",
.id_table = matrox_w1_tbl,
.probe = matrox_w1_probe,
.remove = matrox_w1_remove,
};
module_pci_driver(matrox_w1_pci_driver);
MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire protocol) over VGA DDC(matrox gpio).");
MODULE_LICENSE("GPL");
+180
View File
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Luotao Fu, kernel@pengutronix.de
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/w1.h>
/*
* MXC W1 Register offsets
*/
#define MXC_W1_CONTROL 0x00
# define MXC_W1_CONTROL_RDST BIT(3)
# define MXC_W1_CONTROL_WR(x) BIT(5 - (x))
# define MXC_W1_CONTROL_PST BIT(6)
# define MXC_W1_CONTROL_RPP BIT(7)
#define MXC_W1_TIME_DIVIDER 0x02
#define MXC_W1_RESET 0x04
# define MXC_W1_RESET_RST BIT(0)
struct mxc_w1_device {
void __iomem *regs;
struct clk *clk;
struct w1_bus_master bus_master;
};
/*
* this is the low level routine to
* reset the device on the One Wire interface
* on the hardware
*/
static u8 mxc_w1_ds2_reset_bus(void *data)
{
struct mxc_w1_device *dev = data;
ktime_t timeout;
writeb(MXC_W1_CONTROL_RPP, dev->regs + MXC_W1_CONTROL);
/* Wait for reset sequence 511+512us, use 1500us for sure */
timeout = ktime_add_us(ktime_get(), 1500);
udelay(511 + 512);
do {
u8 ctrl = readb(dev->regs + MXC_W1_CONTROL);
/* PST bit is valid after the RPP bit is self-cleared */
if (!(ctrl & MXC_W1_CONTROL_RPP))
return !(ctrl & MXC_W1_CONTROL_PST);
} while (ktime_before(ktime_get(), timeout));
return 1;
}
/*
* this is the low level routine to read/write a bit on the One Wire
* interface on the hardware. It does write 0 if parameter bit is set
* to 0, otherwise a write 1/read.
*/
static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
{
struct mxc_w1_device *dev = data;
ktime_t timeout;
writeb(MXC_W1_CONTROL_WR(bit), dev->regs + MXC_W1_CONTROL);
/* Wait for read/write bit (60us, Max 120us), use 200us for sure */
timeout = ktime_add_us(ktime_get(), 200);
udelay(60);
do {
u8 ctrl = readb(dev->regs + MXC_W1_CONTROL);
/* RDST bit is valid after the WR1/RD bit is self-cleared */
if (!(ctrl & MXC_W1_CONTROL_WR(bit)))
return !!(ctrl & MXC_W1_CONTROL_RDST);
} while (ktime_before(ktime_get(), timeout));
return 0;
}
static int mxc_w1_probe(struct platform_device *pdev)
{
struct mxc_w1_device *mdev;
unsigned long clkrate;
unsigned int clkdiv;
int err;
mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device),
GFP_KERNEL);
if (!mdev)
return -ENOMEM;
mdev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(mdev->clk))
return PTR_ERR(mdev->clk);
err = clk_prepare_enable(mdev->clk);
if (err)
return err;
clkrate = clk_get_rate(mdev->clk);
if (clkrate < 10000000)
dev_warn(&pdev->dev,
"Low clock frequency causes improper function\n");
clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000);
clkrate /= clkdiv;
if ((clkrate < 980000) || (clkrate > 1020000))
dev_warn(&pdev->dev,
"Incorrect time base frequency %lu Hz\n", clkrate);
mdev->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mdev->regs)) {
err = PTR_ERR(mdev->regs);
goto out_disable_clk;
}
/* Software reset 1-Wire module */
writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET);
writeb(0, mdev->regs + MXC_W1_RESET);
writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER);
mdev->bus_master.data = mdev;
mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus;
mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit;
platform_set_drvdata(pdev, mdev);
err = w1_add_master_device(&mdev->bus_master);
if (err)
goto out_disable_clk;
return 0;
out_disable_clk:
clk_disable_unprepare(mdev->clk);
return err;
}
/*
* disassociate the w1 device from the driver
*/
static void mxc_w1_remove(struct platform_device *pdev)
{
struct mxc_w1_device *mdev = platform_get_drvdata(pdev);
w1_remove_master_device(&mdev->bus_master);
clk_disable_unprepare(mdev->clk);
}
static const struct of_device_id mxc_w1_dt_ids[] = {
{ .compatible = "fsl,imx21-owire" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids);
static struct platform_driver mxc_w1_driver = {
.driver = {
.name = "mxc_w1",
.of_match_table = mxc_w1_dt_ids,
},
.probe = mxc_w1_probe,
.remove = mxc_w1_remove,
};
module_platform_driver(mxc_w1_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale Semiconductors Inc");
MODULE_DESCRIPTION("Driver for One-Wire on MXC");
+681
View File
@@ -0,0 +1,681 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2007,2012 Texas Instruments, Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/w1.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_SINGLE BIT(7)
#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK BIT(6)
#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE BIT(5)
#define OMAP_HDQ_CTRL_STATUS_GO BIT(4)
#define OMAP_HDQ_CTRL_STATUS_PRESENCE BIT(3)
#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION BIT(2)
#define OMAP_HDQ_CTRL_STATUS_DIR BIT(1)
#define OMAP_HDQ_INT_STATUS 0x10
#define OMAP_HDQ_INT_STATUS_TXCOMPLETE BIT(2)
#define OMAP_HDQ_INT_STATUS_RXCOMPLETE BIT(1)
#define OMAP_HDQ_INT_STATUS_TIMEOUT BIT(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;
module_param(w1_id, int, 0400);
MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection in HDQ mode");
struct hdq_data {
struct device *dev;
void __iomem *hdq_base;
/* lock read/write/break operations */
struct mutex hdq_mutex;
/* interrupt status and a lock for it */
u8 hdq_irqstatus;
spinlock_t hdq_spinlock;
/* mode: 0-HDQ 1-W1 */
int mode;
};
/* HDQ register I/O routines */
static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset)
{
return __raw_readl(hdq_data->hdq_base + offset);
}
static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val)
{
__raw_writel(val, hdq_data->hdq_base + offset);
}
static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset,
u8 val, u8 mask)
{
u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask)
| (val & mask);
__raw_writel(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;
}
/* Clear saved irqstatus after using an interrupt */
static u8 hdq_reset_irqstatus(struct hdq_data *hdq_data, u8 bits)
{
unsigned long irqflags;
u8 status;
spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
status = hdq_data->hdq_irqstatus;
/* this is a read-modify-write */
hdq_data->hdq_irqstatus &= ~bits;
spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
return status;
}
/* 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;
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
ret = -EINTR;
goto rtn;
}
if (hdq_data->hdq_irqstatus)
dev_err(hdq_data->dev, "TX irqstatus not cleared (%02x)\n",
hdq_data->hdq_irqstatus);
*status = 0;
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_INT_STATUS_TXCOMPLETE),
OMAP_HDQ_TIMEOUT);
*status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE);
if (ret == 0) {
dev_dbg(hdq_data->dev, "TX wait elapsed\n");
ret = -ETIMEDOUT;
goto out;
}
/* check irqstatus */
if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {
dev_dbg(hdq_data->dev, "timeout waiting for"
" TXCOMPLETE/RXCOMPLETE, %x\n", *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\n", tmp_status);
}
out:
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
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\n", 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;
}
/* W1 search callback function in HDQ mode */
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);
}
/* Issue break pulse to the device */
static int omap_hdq_break(struct hdq_data *hdq_data)
{
int ret = 0;
u8 tmp_status;
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;
}
if (hdq_data->hdq_irqstatus)
dev_err(hdq_data->dev, "break irqstatus not cleared (%02x)\n",
hdq_data->hdq_irqstatus);
/* 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_INT_STATUS_TIMEOUT),
OMAP_HDQ_TIMEOUT);
tmp_status = hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TIMEOUT);
if (ret == 0) {
dev_dbg(hdq_data->dev, "break wait elapsed\n");
ret = -EINTR;
goto out;
}
/* check irqstatus */
if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) {
dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x\n",
tmp_status);
ret = -ETIMEDOUT;
goto out;
}
/*
* check for the presence detect bit to get
* set to show that the slave is responding
*/
if (!(hdq_reg_in(hdq_data, OMAP_HDQ_CTRL_STATUS) &
OMAP_HDQ_CTRL_STATUS_PRESENCE)) {
dev_dbg(hdq_data->dev, "Presence bit not set\n");
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\n", 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;
ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (ret < 0) {
ret = -EINTR;
goto rtn;
}
if (pm_runtime_suspended(hdq_data->dev)) {
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.
*/
wait_event_timeout(hdq_wait_queue,
(hdq_data->hdq_irqstatus
& (OMAP_HDQ_INT_STATUS_RXCOMPLETE |
OMAP_HDQ_INT_STATUS_TIMEOUT)),
OMAP_HDQ_TIMEOUT);
status = hdq_reset_irqstatus(hdq_data,
OMAP_HDQ_INT_STATUS_RXCOMPLETE |
OMAP_HDQ_INT_STATUS_TIMEOUT);
hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0,
OMAP_HDQ_CTRL_STATUS_DIR);
/* check irqstatus */
if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
dev_dbg(hdq_data->dev, "timeout waiting for"
" RXCOMPLETE, %x", status);
ret = -ETIMEDOUT;
goto out;
}
} else { /* interrupt had occurred before hdq_read_byte was called */
hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
}
/* 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 ret;
}
/*
* W1 triplet callback function - used for searching ROM addresses.
* Registered only when controller is in 1-wire mode.
*/
static u8 omap_w1_triplet(void *_hdq, u8 bdir)
{
u8 id_bit, comp_bit;
int err;
u8 ret = 0x3; /* no slaves responded */
struct hdq_data *hdq_data = _hdq;
u8 ctrl = OMAP_HDQ_CTRL_STATUS_SINGLE | OMAP_HDQ_CTRL_STATUS_GO |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK;
u8 mask = ctrl | OMAP_HDQ_CTRL_STATUS_DIR;
err = pm_runtime_get_sync(hdq_data->dev);
if (err < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return err;
}
err = mutex_lock_interruptible(&hdq_data->hdq_mutex);
if (err < 0) {
dev_dbg(hdq_data->dev, "Could not acquire mutex\n");
goto rtn;
}
/* read id_bit */
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
err = wait_event_timeout(hdq_wait_queue,
(hdq_data->hdq_irqstatus
& OMAP_HDQ_INT_STATUS_RXCOMPLETE),
OMAP_HDQ_TIMEOUT);
/* Must clear irqstatus for another RXCOMPLETE interrupt */
hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
if (err == 0) {
dev_dbg(hdq_data->dev, "RX wait elapsed\n");
goto out;
}
id_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01);
/* read comp_bit */
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS,
ctrl | OMAP_HDQ_CTRL_STATUS_DIR, mask);
err = wait_event_timeout(hdq_wait_queue,
(hdq_data->hdq_irqstatus
& OMAP_HDQ_INT_STATUS_RXCOMPLETE),
OMAP_HDQ_TIMEOUT);
/* Must clear irqstatus for another RXCOMPLETE interrupt */
hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_RXCOMPLETE);
if (err == 0) {
dev_dbg(hdq_data->dev, "RX wait elapsed\n");
goto out;
}
comp_bit = (hdq_reg_in(_hdq, OMAP_HDQ_RX_DATA) & 0x01);
if (id_bit && comp_bit) {
ret = 0x03; /* no slaves responded */
goto out;
}
if (!id_bit && !comp_bit) {
/* Both bits are valid, take the direction given */
ret = bdir ? 0x04 : 0;
} else {
/* Only one bit is valid, take that direction */
bdir = id_bit;
ret = id_bit ? 0x05 : 0x02;
}
/* write bdir bit */
hdq_reg_out(_hdq, OMAP_HDQ_TX_DATA, bdir);
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, ctrl, mask);
err = wait_event_timeout(hdq_wait_queue,
(hdq_data->hdq_irqstatus
& OMAP_HDQ_INT_STATUS_TXCOMPLETE),
OMAP_HDQ_TIMEOUT);
/* Must clear irqstatus for another TXCOMPLETE interrupt */
hdq_reset_irqstatus(hdq_data, OMAP_HDQ_INT_STATUS_TXCOMPLETE);
if (err == 0) {
dev_dbg(hdq_data->dev, "TX wait elapsed\n");
goto out;
}
hdq_reg_merge(_hdq, OMAP_HDQ_CTRL_STATUS, 0,
OMAP_HDQ_CTRL_STATUS_SINGLE);
out:
mutex_unlock(&hdq_data->hdq_mutex);
rtn:
pm_runtime_put_autosuspend(hdq_data->dev);
return ret;
}
/* reset callback */
static u8 omap_w1_reset_bus(void *_hdq)
{
struct hdq_data *hdq_data = _hdq;
int err;
err = pm_runtime_get_sync(hdq_data->dev);
if (err < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return err;
}
omap_hdq_break(hdq_data);
pm_runtime_put_autosuspend(hdq_data->dev);
return 0;
}
/* 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 = pm_runtime_get_sync(hdq_data->dev);
if (ret < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return -1;
}
ret = hdq_read_byte(hdq_data, &val);
if (ret)
val = -1;
pm_runtime_put_autosuspend(hdq_data->dev);
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;
ret = pm_runtime_get_sync(hdq_data->dev);
if (ret < 0) {
pm_runtime_put_noidle(hdq_data->dev);
return;
}
/*
* We need to reset the slave before
* issuing the SKIP ROM command, else
* the slave will not work.
*/
if (byte == W1_SKIP_ROM)
omap_hdq_break(hdq_data);
ret = hdq_write_byte(hdq_data, byte, &status);
if (ret < 0) {
dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);
goto out_err;
}
out_err:
pm_runtime_put_autosuspend(hdq_data->dev);
}
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,
};
static int __maybe_unused omap_hdq_runtime_suspend(struct device *dev)
{
struct hdq_data *hdq_data = dev_get_drvdata(dev);
hdq_reg_out(hdq_data, 0, hdq_data->mode);
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
return 0;
}
static int __maybe_unused omap_hdq_runtime_resume(struct device *dev)
{
struct hdq_data *hdq_data = dev_get_drvdata(dev);
/* select HDQ/1W mode & enable clocks */
hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS,
OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK |
hdq_data->mode);
hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS);
return 0;
}
static const struct dev_pm_ops omap_hdq_pm_ops = {
SET_RUNTIME_PM_OPS(omap_hdq_runtime_suspend,
omap_hdq_runtime_resume, NULL)
};
static int omap_hdq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct hdq_data *hdq_data;
int ret, irq;
u8 rev;
const char *mode;
hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL);
if (!hdq_data)
return -ENOMEM;
hdq_data->dev = dev;
platform_set_drvdata(pdev, hdq_data);
hdq_data->hdq_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdq_data->hdq_base))
return PTR_ERR(hdq_data->hdq_base);
mutex_init(&hdq_data->hdq_mutex);
ret = of_property_read_string(pdev->dev.of_node, "ti,mode", &mode);
if (ret < 0 || !strcmp(mode, "hdq")) {
hdq_data->mode = 0;
omap_w1_master.search = omap_w1_search_bus;
} else {
hdq_data->mode = 1;
omap_w1_master.triplet = omap_w1_triplet;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 300);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
pm_runtime_put_noidle(&pdev->dev);
dev_dbg(&pdev->dev, "pm_runtime_get_sync failed\n");
goto err_w1;
}
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) {
dev_dbg(&pdev->dev, "Failed to get IRQ: %d\n", irq);
ret = irq;
goto err_irq;
}
ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data);
if (ret < 0) {
dev_dbg(&pdev->dev, "could not request irq\n");
goto err_irq;
}
omap_hdq_break(hdq_data);
pm_runtime_put_autosuspend(&pdev->dev);
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_irq:
pm_runtime_put_sync(&pdev->dev);
err_w1:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
static void omap_hdq_remove(struct platform_device *pdev)
{
int active;
active = pm_runtime_get_sync(&pdev->dev);
if (active < 0)
pm_runtime_put_noidle(&pdev->dev);
w1_remove_master_device(&omap_w1_master);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (active >= 0)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
static const struct of_device_id omap_hdq_dt_ids[] = {
{ .compatible = "ti,omap3-1w" },
{ .compatible = "ti,am4372-hdq" },
{}
};
MODULE_DEVICE_TABLE(of, omap_hdq_dt_ids);
static struct platform_driver omap_hdq_driver = {
.probe = omap_hdq_probe,
.remove = omap_hdq_remove,
.driver = {
.name = "omap_hdq",
.of_match_table = omap_hdq_dt_ids,
.pm = &omap_hdq_pm_ops,
},
};
module_platform_driver(omap_hdq_driver);
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("HDQ-1W driver Library");
MODULE_LICENSE("GPL");
+125
View File
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0
/*
* sgi_w1.c - w1 master driver for one wire support in SGI ASICs
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/sgi-w1.h>
#include <linux/w1.h>
#define MCR_RD_DATA BIT(0)
#define MCR_DONE BIT(1)
#define MCR_PACK(pulse, sample) (((pulse) << 10) | ((sample) << 2))
struct sgi_w1_device {
u32 __iomem *mcr;
struct w1_bus_master bus_master;
char dev_id[64];
};
static u8 sgi_w1_wait(u32 __iomem *mcr)
{
u32 mcr_val;
do {
mcr_val = readl(mcr);
} while (!(mcr_val & MCR_DONE));
return (mcr_val & MCR_RD_DATA) ? 1 : 0;
}
/*
* this is the low level routine to
* reset the device on the One Wire interface
* on the hardware
*/
static u8 sgi_w1_reset_bus(void *data)
{
struct sgi_w1_device *dev = data;
u8 ret;
writel(MCR_PACK(520, 65), dev->mcr);
ret = sgi_w1_wait(dev->mcr);
udelay(500); /* recovery time */
return ret;
}
/*
* this is the low level routine to read/write a bit on the One Wire
* interface on the hardware. It does write 0 if parameter bit is set
* to 0, otherwise a write 1/read.
*/
static u8 sgi_w1_touch_bit(void *data, u8 bit)
{
struct sgi_w1_device *dev = data;
u8 ret;
if (bit)
writel(MCR_PACK(6, 13), dev->mcr);
else
writel(MCR_PACK(80, 30), dev->mcr);
ret = sgi_w1_wait(dev->mcr);
if (bit)
udelay(100); /* recovery */
return ret;
}
static int sgi_w1_probe(struct platform_device *pdev)
{
struct sgi_w1_device *sdev;
struct sgi_w1_platform_data *pdata;
sdev = devm_kzalloc(&pdev->dev, sizeof(struct sgi_w1_device),
GFP_KERNEL);
if (!sdev)
return -ENOMEM;
sdev->mcr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sdev->mcr))
return PTR_ERR(sdev->mcr);
sdev->bus_master.data = sdev;
sdev->bus_master.reset_bus = sgi_w1_reset_bus;
sdev->bus_master.touch_bit = sgi_w1_touch_bit;
pdata = dev_get_platdata(&pdev->dev);
if (pdata) {
strscpy(sdev->dev_id, pdata->dev_id, sizeof(sdev->dev_id));
sdev->bus_master.dev_id = sdev->dev_id;
}
platform_set_drvdata(pdev, sdev);
return w1_add_master_device(&sdev->bus_master);
}
/*
* disassociate the w1 device from the driver
*/
static void sgi_w1_remove(struct platform_device *pdev)
{
struct sgi_w1_device *sdev = platform_get_drvdata(pdev);
w1_remove_master_device(&sdev->bus_master);
}
static struct platform_driver sgi_w1_driver = {
.driver = {
.name = "sgi_w1",
},
.probe = sgi_w1_probe,
.remove = sgi_w1_remove,
};
module_platform_driver(sgi_w1_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_DESCRIPTION("Driver for One-Wire IP in SGI ASICs");
+155
View File
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1-gpio - GPIO w1 bus master driver
*
* Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi>
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/types.h>
#include <linux/w1.h>
struct w1_gpio_ddata {
struct gpio_desc *gpiod;
struct gpio_desc *pullup_gpiod;
unsigned int pullup_duration;
};
static u8 w1_gpio_set_pullup(void *data, int delay)
{
struct w1_gpio_ddata *ddata = data;
if (delay) {
ddata->pullup_duration = delay;
} else {
if (ddata->pullup_duration) {
/*
* This will OVERRIDE open drain emulation and force-pull
* the line high for some time.
*/
gpiod_set_raw_value(ddata->gpiod, 1);
msleep(ddata->pullup_duration);
/*
* This will simply set the line as input since we are doing
* open drain emulation in the GPIO library.
*/
gpiod_set_value(ddata->gpiod, 1);
}
ddata->pullup_duration = 0;
}
return 0;
}
static void w1_gpio_write_bit(void *data, u8 bit)
{
struct w1_gpio_ddata *ddata = data;
gpiod_set_value(ddata->gpiod, bit);
}
static u8 w1_gpio_read_bit(void *data)
{
struct w1_gpio_ddata *ddata = data;
return gpiod_get_value(ddata->gpiod) ? 1 : 0;
}
static int w1_gpio_probe(struct platform_device *pdev)
{
struct w1_bus_master *master;
struct w1_gpio_ddata *ddata;
struct device *dev = &pdev->dev;
/* Enforce open drain mode by default */
enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
int err;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
/*
* This parameter means that something else than the gpiolib has
* already set the line into open drain mode, so we should just
* driver it high/low like we are in full control of the line and
* open drain will happen transparently.
*/
if (device_property_present(dev, "linux,open-drain"))
gflags = GPIOD_OUT_LOW;
master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
ddata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags);
if (IS_ERR(ddata->gpiod))
return dev_err_probe(dev, PTR_ERR(ddata->gpiod), "gpio_request (pin) failed\n");
ddata->pullup_gpiod =
devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_LOW);
if (IS_ERR(ddata->pullup_gpiod))
return dev_err_probe(dev, PTR_ERR(ddata->pullup_gpiod),
"gpio_request (ext_pullup_enable_pin) failed\n");
master->data = ddata;
master->read_bit = w1_gpio_read_bit;
gpiod_direction_output(ddata->gpiod, 1);
master->write_bit = w1_gpio_write_bit;
/*
* If we are using open drain emulation from the GPIO library,
* we need to use this pullup function that hammers the line
* high using a raw accessor to provide pull-up for the w1
* line.
*/
if (gflags == GPIOD_OUT_LOW_OPEN_DRAIN)
master->set_pullup = w1_gpio_set_pullup;
err = w1_add_master_device(master);
if (err)
return dev_err_probe(dev, err, "w1_add_master device failed\n");
gpiod_set_value(ddata->pullup_gpiod, 1);
platform_set_drvdata(pdev, master);
return 0;
}
static void w1_gpio_remove(struct platform_device *pdev)
{
struct w1_bus_master *master = platform_get_drvdata(pdev);
struct w1_gpio_ddata *ddata = master->data;
gpiod_set_value(ddata->pullup_gpiod, 0);
w1_remove_master_device(master);
}
static const struct of_device_id w1_gpio_dt_ids[] = {
{ .compatible = "w1-gpio" },
{}
};
MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
static struct platform_driver w1_gpio_driver = {
.driver = {
.name = "w1-gpio",
.of_match_table = w1_gpio_dt_ids,
},
.probe = w1_gpio_probe,
.remove = w1_gpio_remove,
};
module_platform_driver(w1_gpio_driver);
MODULE_DESCRIPTION("GPIO w1 bus master driver");
MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
MODULE_LICENSE("GPL");
+415
View File
@@ -0,0 +1,415 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* w1-uart - UART 1-Wire bus driver
*
* Uses the UART interface (via Serial Device Bus) to create the 1-Wire
* timing patterns. Implements the following 1-Wire master interface:
*
* - reset_bus: requests baud-rate 9600
*
* - touch_bit: requests baud-rate 115200
*
* Author: Christoph Winklhofer <cj.winklhofer@gmail.com>
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/serdev.h>
#include <linux/w1.h>
/* UART packet contains start and stop bit */
#define W1_UART_BITS_PER_PACKET (BITS_PER_BYTE + 2)
/* Timeout to wait for completion of serdev-receive */
#define W1_UART_TIMEOUT msecs_to_jiffies(500)
/**
* struct w1_uart_config - configuration for 1-Wire operation
* @baudrate: baud-rate returned from serdev
* @delay_us: delay to complete a 1-Wire cycle (in us)
* @tx_byte: byte to generate 1-Wire timing pattern
*/
struct w1_uart_config {
unsigned int baudrate;
unsigned int delay_us;
u8 tx_byte;
};
/**
* struct w1_uart_device - 1-Wire UART device structure
* @serdev: serial device
* @bus: w1-bus master
* @cfg_reset: config for 1-Wire reset
* @cfg_touch_0: config for 1-Wire write-0 cycle
* @cfg_touch_1: config for 1-Wire write-1 and read cycle
* @rx_byte_received: completion for serdev receive
* @rx_mutex: mutex to protect rx_err and rx_byte
* @rx_err: indicates an error in serdev-receive
* @rx_byte: result byte from serdev-receive
*/
struct w1_uart_device {
struct serdev_device *serdev;
struct w1_bus_master bus;
struct w1_uart_config cfg_reset;
struct w1_uart_config cfg_touch_0;
struct w1_uart_config cfg_touch_1;
struct completion rx_byte_received;
/*
* protect rx_err and rx_byte from concurrent access in
* w1-callbacks and serdev-receive.
*/
struct mutex rx_mutex;
int rx_err;
u8 rx_byte;
};
/**
* struct w1_uart_limits - limits for 1-Wire operations
* @baudrate: Requested baud-rate to create 1-Wire timing pattern
* @bit_min_us: minimum time for a bit (in us)
* @bit_max_us: maximum time for a bit (in us)
* @sample_us: timespan to sample 1-Wire response
* @cycle_us: duration of the 1-Wire cycle
*/
struct w1_uart_limits {
unsigned int baudrate;
unsigned int bit_min_us;
unsigned int bit_max_us;
unsigned int sample_us;
unsigned int cycle_us;
};
static inline unsigned int baud_to_bit_ns(unsigned int baud)
{
return NSEC_PER_SEC / baud;
}
static inline unsigned int to_ns(unsigned int us)
{
return us * NSEC_PER_USEC;
}
/*
* Set baud-rate, delay and tx-byte to create a 1-Wire pulse and adapt
* the tx-byte according to the actual baud-rate.
*
* Reject when:
* - time for a bit outside min/max range
* - a 1-Wire response is not detectable for sent byte
*/
static int w1_uart_set_config(struct serdev_device *serdev,
const struct w1_uart_limits *limits,
struct w1_uart_config *w1cfg)
{
unsigned int packet_ns;
unsigned int bits_low;
unsigned int bit_ns;
unsigned int low_ns;
w1cfg->baudrate = serdev_device_set_baudrate(serdev, limits->baudrate);
if (w1cfg->baudrate == 0)
return -EINVAL;
/* Compute in nanoseconds for accuracy */
bit_ns = baud_to_bit_ns(w1cfg->baudrate);
bits_low = to_ns(limits->bit_min_us) / bit_ns;
/* start bit is always low */
low_ns = bit_ns * (bits_low + 1);
if (low_ns < to_ns(limits->bit_min_us))
return -EINVAL;
if (low_ns > to_ns(limits->bit_max_us))
return -EINVAL;
/* 1-Wire response detectable for sent byte */
if (limits->sample_us > 0 &&
bit_ns * BITS_PER_BYTE < low_ns + to_ns(limits->sample_us))
return -EINVAL;
/* delay: 1-Wire cycle takes longer than the UART packet */
packet_ns = bit_ns * W1_UART_BITS_PER_PACKET;
w1cfg->delay_us = 0;
if (to_ns(limits->cycle_us) > packet_ns)
w1cfg->delay_us =
(to_ns(limits->cycle_us) - packet_ns) / NSEC_PER_USEC;
/* byte to create 1-Wire pulse */
w1cfg->tx_byte = 0xff << bits_low;
return 0;
}
/*
* Configuration for reset and presence detect
* - bit_min_us is 480us, add margin and use 485us
* - limits for sample time 60us-75us, use 65us
*/
static int w1_uart_set_config_reset(struct w1_uart_device *w1dev)
{
struct serdev_device *serdev = w1dev->serdev;
struct device_node *np = serdev->dev.of_node;
struct w1_uart_limits limits = { .baudrate = 9600,
.bit_min_us = 485,
.bit_max_us = 640,
.sample_us = 65,
.cycle_us = 960 };
of_property_read_u32(np, "reset-bps", &limits.baudrate);
return w1_uart_set_config(serdev, &limits, &w1dev->cfg_reset);
}
/*
* Configuration for write-0 cycle (touch bit 0)
* - bit_min_us is 60us, add margin and use 65us
* - no sampling required, sample_us = 0
*/
static int w1_uart_set_config_touch_0(struct w1_uart_device *w1dev)
{
struct serdev_device *serdev = w1dev->serdev;
struct device_node *np = serdev->dev.of_node;
struct w1_uart_limits limits = { .baudrate = 115200,
.bit_min_us = 65,
.bit_max_us = 120,
.sample_us = 0,
.cycle_us = 70 };
of_property_read_u32(np, "write-0-bps", &limits.baudrate);
return w1_uart_set_config(serdev, &limits, &w1dev->cfg_touch_0);
}
/*
* Configuration for write-1 and read cycle (touch bit 1)
* - bit_min_us is 5us, add margin and use 6us
* - limits for sample time 5us-15us, use 15us
*/
static int w1_uart_set_config_touch_1(struct w1_uart_device *w1dev)
{
struct serdev_device *serdev = w1dev->serdev;
struct device_node *np = serdev->dev.of_node;
struct w1_uart_limits limits = { .baudrate = 115200,
.bit_min_us = 6,
.bit_max_us = 15,
.sample_us = 15,
.cycle_us = 70 };
of_property_read_u32(np, "write-1-bps", &limits.baudrate);
return w1_uart_set_config(serdev, &limits, &w1dev->cfg_touch_1);
}
/*
* Configure and open the serial device
*/
static int w1_uart_serdev_open(struct w1_uart_device *w1dev)
{
struct serdev_device *serdev = w1dev->serdev;
struct device *dev = &serdev->dev;
int ret;
ret = devm_serdev_device_open(dev, serdev);
if (ret < 0)
return ret;
ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
if (ret < 0) {
dev_err(dev, "set parity failed\n");
return ret;
}
ret = w1_uart_set_config_reset(w1dev);
if (ret < 0) {
dev_err(dev, "config for reset failed\n");
return ret;
}
ret = w1_uart_set_config_touch_0(w1dev);
if (ret < 0) {
dev_err(dev, "config for touch-0 failed\n");
return ret;
}
ret = w1_uart_set_config_touch_1(w1dev);
if (ret < 0) {
dev_err(dev, "config for touch-1 failed\n");
return ret;
}
serdev_device_set_flow_control(serdev, false);
return 0;
}
/*
* Send one byte (tx_byte) and read one byte (rx_byte) via serdev.
*/
static int w1_uart_serdev_tx_rx(struct w1_uart_device *w1dev,
const struct w1_uart_config *w1cfg, u8 *rx_byte)
{
struct serdev_device *serdev = w1dev->serdev;
int ret;
serdev_device_write_flush(serdev);
serdev_device_set_baudrate(serdev, w1cfg->baudrate);
/* write and immediately read one byte */
reinit_completion(&w1dev->rx_byte_received);
ret = serdev_device_write_buf(serdev, &w1cfg->tx_byte, 1);
if (ret != 1)
return -EIO;
ret = wait_for_completion_interruptible_timeout(
&w1dev->rx_byte_received, W1_UART_TIMEOUT);
if (ret <= 0)
return -EIO;
/* locking could fail when serdev is unexpectedly receiving. */
if (!mutex_trylock(&w1dev->rx_mutex))
return -EIO;
ret = w1dev->rx_err;
if (ret == 0)
*rx_byte = w1dev->rx_byte;
mutex_unlock(&w1dev->rx_mutex);
if (w1cfg->delay_us > 0)
fsleep(w1cfg->delay_us);
return ret;
}
static size_t w1_uart_serdev_receive_buf(struct serdev_device *serdev,
const u8 *buf, size_t count)
{
struct w1_uart_device *w1dev = serdev_device_get_drvdata(serdev);
mutex_lock(&w1dev->rx_mutex);
/* sent a single byte and receive one single byte */
if (count == 1) {
w1dev->rx_byte = buf[0];
w1dev->rx_err = 0;
} else {
w1dev->rx_err = -EIO;
}
mutex_unlock(&w1dev->rx_mutex);
complete(&w1dev->rx_byte_received);
return count;
}
static const struct serdev_device_ops w1_uart_serdev_ops = {
.receive_buf = w1_uart_serdev_receive_buf,
.write_wakeup = serdev_device_write_wakeup,
};
/*
* 1-wire reset and presence detect: A present slave will manipulate
* the received byte by pulling the 1-Wire low.
*/
static u8 w1_uart_reset_bus(void *data)
{
struct w1_uart_device *w1dev = data;
const struct w1_uart_config *w1cfg = &w1dev->cfg_reset;
int ret;
u8 val;
ret = w1_uart_serdev_tx_rx(w1dev, w1cfg, &val);
if (ret < 0)
return -1;
/* Device present (0) or no device (1) */
return val != w1cfg->tx_byte ? 0 : 1;
}
/*
* 1-Wire read and write cycle: Only the read-0 manipulates the
* received byte, all others left the line untouched.
*/
static u8 w1_uart_touch_bit(void *data, u8 bit)
{
struct w1_uart_device *w1dev = data;
const struct w1_uart_config *w1cfg = bit ? &w1dev->cfg_touch_1 :
&w1dev->cfg_touch_0;
int ret;
u8 val;
ret = w1_uart_serdev_tx_rx(w1dev, w1cfg, &val);
/* return inactive bus state on error */
if (ret < 0)
return 1;
return val == w1cfg->tx_byte ? 1 : 0;
}
static int w1_uart_probe(struct serdev_device *serdev)
{
struct device *dev = &serdev->dev;
struct w1_uart_device *w1dev;
int ret;
w1dev = devm_kzalloc(dev, sizeof(*w1dev), GFP_KERNEL);
if (!w1dev)
return -ENOMEM;
w1dev->bus.data = w1dev;
w1dev->bus.reset_bus = w1_uart_reset_bus;
w1dev->bus.touch_bit = w1_uart_touch_bit;
w1dev->serdev = serdev;
init_completion(&w1dev->rx_byte_received);
mutex_init(&w1dev->rx_mutex);
serdev_device_set_drvdata(serdev, w1dev);
serdev_device_set_client_ops(serdev, &w1_uart_serdev_ops);
ret = w1_uart_serdev_open(w1dev);
if (ret < 0)
return ret;
return w1_add_master_device(&w1dev->bus);
}
static void w1_uart_remove(struct serdev_device *serdev)
{
struct w1_uart_device *w1dev = serdev_device_get_drvdata(serdev);
/*
* Waits until w1-uart callbacks are finished, serdev is closed
* and its device data released automatically by devres (waits
* until serdev-receive is finished).
*/
w1_remove_master_device(&w1dev->bus);
}
static const struct of_device_id w1_uart_of_match[] = {
{ .compatible = "w1-uart" },
{},
};
MODULE_DEVICE_TABLE(of, w1_uart_of_match);
static struct serdev_device_driver w1_uart_driver = {
.driver = {
.name = "w1-uart",
.of_match_table = w1_uart_of_match,
},
.probe = w1_uart_probe,
.remove = w1_uart_remove,
};
module_serdev_device_driver(w1_uart_driver);
MODULE_DESCRIPTION("UART w1 bus driver");
MODULE_AUTHOR("Christoph Winklhofer <cj.winklhofer@gmail.com>");
MODULE_LICENSE("GPL");