sgi_w1.c (2765B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * sgi_w1.c - w1 master driver for one wire support in SGI ASICs 4 */ 5 6#include <linux/clk.h> 7#include <linux/delay.h> 8#include <linux/io.h> 9#include <linux/jiffies.h> 10#include <linux/module.h> 11#include <linux/mod_devicetable.h> 12#include <linux/platform_device.h> 13#include <linux/platform_data/sgi-w1.h> 14 15#include <linux/w1.h> 16 17#define MCR_RD_DATA BIT(0) 18#define MCR_DONE BIT(1) 19 20#define MCR_PACK(pulse, sample) (((pulse) << 10) | ((sample) << 2)) 21 22struct sgi_w1_device { 23 u32 __iomem *mcr; 24 struct w1_bus_master bus_master; 25 char dev_id[64]; 26}; 27 28static u8 sgi_w1_wait(u32 __iomem *mcr) 29{ 30 u32 mcr_val; 31 32 do { 33 mcr_val = readl(mcr); 34 } while (!(mcr_val & MCR_DONE)); 35 36 return (mcr_val & MCR_RD_DATA) ? 1 : 0; 37} 38 39/* 40 * this is the low level routine to 41 * reset the device on the One Wire interface 42 * on the hardware 43 */ 44static u8 sgi_w1_reset_bus(void *data) 45{ 46 struct sgi_w1_device *dev = data; 47 u8 ret; 48 49 writel(MCR_PACK(520, 65), dev->mcr); 50 ret = sgi_w1_wait(dev->mcr); 51 udelay(500); /* recovery time */ 52 return ret; 53} 54 55/* 56 * this is the low level routine to read/write a bit on the One Wire 57 * interface on the hardware. It does write 0 if parameter bit is set 58 * to 0, otherwise a write 1/read. 59 */ 60static u8 sgi_w1_touch_bit(void *data, u8 bit) 61{ 62 struct sgi_w1_device *dev = data; 63 u8 ret; 64 65 if (bit) 66 writel(MCR_PACK(6, 13), dev->mcr); 67 else 68 writel(MCR_PACK(80, 30), dev->mcr); 69 70 ret = sgi_w1_wait(dev->mcr); 71 if (bit) 72 udelay(100); /* recovery */ 73 return ret; 74} 75 76static int sgi_w1_probe(struct platform_device *pdev) 77{ 78 struct sgi_w1_device *sdev; 79 struct sgi_w1_platform_data *pdata; 80 81 sdev = devm_kzalloc(&pdev->dev, sizeof(struct sgi_w1_device), 82 GFP_KERNEL); 83 if (!sdev) 84 return -ENOMEM; 85 86 sdev->mcr = devm_platform_ioremap_resource(pdev, 0); 87 if (IS_ERR(sdev->mcr)) 88 return PTR_ERR(sdev->mcr); 89 90 sdev->bus_master.data = sdev; 91 sdev->bus_master.reset_bus = sgi_w1_reset_bus; 92 sdev->bus_master.touch_bit = sgi_w1_touch_bit; 93 94 pdata = dev_get_platdata(&pdev->dev); 95 if (pdata) { 96 strlcpy(sdev->dev_id, pdata->dev_id, sizeof(sdev->dev_id)); 97 sdev->bus_master.dev_id = sdev->dev_id; 98 } 99 100 platform_set_drvdata(pdev, sdev); 101 102 return w1_add_master_device(&sdev->bus_master); 103} 104 105/* 106 * disassociate the w1 device from the driver 107 */ 108static int sgi_w1_remove(struct platform_device *pdev) 109{ 110 struct sgi_w1_device *sdev = platform_get_drvdata(pdev); 111 112 w1_remove_master_device(&sdev->bus_master); 113 114 return 0; 115} 116 117static struct platform_driver sgi_w1_driver = { 118 .driver = { 119 .name = "sgi_w1", 120 }, 121 .probe = sgi_w1_probe, 122 .remove = sgi_w1_remove, 123}; 124module_platform_driver(sgi_w1_driver); 125 126MODULE_LICENSE("GPL"); 127MODULE_AUTHOR("Thomas Bogendoerfer"); 128MODULE_DESCRIPTION("Driver for One-Wire IP in SGI ASICs");