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 22 struct sgi_w1_device { 23 u32 __iomem *mcr; 24 struct w1_bus_master bus_master; 25 char dev_id[64]; 26 }; 27 28 static 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 */ 44 static 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 */ 60 static 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 76 static int sgi_w1_probe(struct platform_device *pdev) 77 { 78 struct sgi_w1_device *sdev; 79 struct sgi_w1_platform_data *pdata; 80 struct resource *res; 81 82 sdev = devm_kzalloc(&pdev->dev, sizeof(struct sgi_w1_device), 83 GFP_KERNEL); 84 if (!sdev) 85 return -ENOMEM; 86 87 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 88 sdev->mcr = devm_ioremap_resource(&pdev->dev, res); 89 if (IS_ERR(sdev->mcr)) 90 return PTR_ERR(sdev->mcr); 91 92 sdev->bus_master.data = sdev; 93 sdev->bus_master.reset_bus = sgi_w1_reset_bus; 94 sdev->bus_master.touch_bit = sgi_w1_touch_bit; 95 96 pdata = dev_get_platdata(&pdev->dev); 97 if (pdata) { 98 strlcpy(sdev->dev_id, pdata->dev_id, sizeof(sdev->dev_id)); 99 sdev->bus_master.dev_id = sdev->dev_id; 100 } 101 102 platform_set_drvdata(pdev, sdev); 103 104 return w1_add_master_device(&sdev->bus_master); 105 } 106 107 /* 108 * disassociate the w1 device from the driver 109 */ 110 static int sgi_w1_remove(struct platform_device *pdev) 111 { 112 struct sgi_w1_device *sdev = platform_get_drvdata(pdev); 113 114 w1_remove_master_device(&sdev->bus_master); 115 116 return 0; 117 } 118 119 static struct platform_driver sgi_w1_driver = { 120 .driver = { 121 .name = "sgi_w1", 122 }, 123 .probe = sgi_w1_probe, 124 .remove = sgi_w1_remove, 125 }; 126 module_platform_driver(sgi_w1_driver); 127 128 MODULE_LICENSE("GPL"); 129 MODULE_AUTHOR("Thomas Bogendoerfer"); 130 MODULE_DESCRIPTION("Driver for One-Wire IP in SGI ASICs"); 131