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