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 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 strscpy(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 */ 108 static void 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 115 static struct platform_driver sgi_w1_driver = { 116 .driver = { 117 .name = "sgi_w1", 118 }, 119 .probe = sgi_w1_probe, 120 .remove_new = sgi_w1_remove, 121 }; 122 module_platform_driver(sgi_w1_driver); 123 124 MODULE_LICENSE("GPL"); 125 MODULE_AUTHOR("Thomas Bogendoerfer"); 126 MODULE_DESCRIPTION("Driver for One-Wire IP in SGI ASICs"); 127