1*e7ec2e32SMichael Buesch /* 2*e7ec2e32SMichael Buesch * Sonics Silicon Backplane 3*e7ec2e32SMichael Buesch * Common SPROM support routines 4*e7ec2e32SMichael Buesch * 5*e7ec2e32SMichael Buesch * Copyright (C) 2005-2008 Michael Buesch <mb@bu3sch.de> 6*e7ec2e32SMichael Buesch * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de> 7*e7ec2e32SMichael Buesch * Copyright (C) 2005 Stefano Brivio <st3@riseup.net> 8*e7ec2e32SMichael Buesch * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org> 9*e7ec2e32SMichael Buesch * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> 10*e7ec2e32SMichael Buesch * 11*e7ec2e32SMichael Buesch * Licensed under the GNU/GPL. See COPYING for details. 12*e7ec2e32SMichael Buesch */ 13*e7ec2e32SMichael Buesch 14*e7ec2e32SMichael Buesch #include "ssb_private.h" 15*e7ec2e32SMichael Buesch 16*e7ec2e32SMichael Buesch 17*e7ec2e32SMichael Buesch static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, 18*e7ec2e32SMichael Buesch size_t sprom_size_words) 19*e7ec2e32SMichael Buesch { 20*e7ec2e32SMichael Buesch int i, pos = 0; 21*e7ec2e32SMichael Buesch 22*e7ec2e32SMichael Buesch for (i = 0; i < sprom_size_words; i++) 23*e7ec2e32SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, 24*e7ec2e32SMichael Buesch "%04X", swab16(sprom[i]) & 0xFFFF); 25*e7ec2e32SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); 26*e7ec2e32SMichael Buesch 27*e7ec2e32SMichael Buesch return pos + 1; 28*e7ec2e32SMichael Buesch } 29*e7ec2e32SMichael Buesch 30*e7ec2e32SMichael Buesch static int hex2sprom(u16 *sprom, const char *dump, size_t len, 31*e7ec2e32SMichael Buesch size_t sprom_size_words) 32*e7ec2e32SMichael Buesch { 33*e7ec2e32SMichael Buesch char tmp[5] = { 0 }; 34*e7ec2e32SMichael Buesch int cnt = 0; 35*e7ec2e32SMichael Buesch unsigned long parsed; 36*e7ec2e32SMichael Buesch 37*e7ec2e32SMichael Buesch if (len < sprom_size_words * 2) 38*e7ec2e32SMichael Buesch return -EINVAL; 39*e7ec2e32SMichael Buesch 40*e7ec2e32SMichael Buesch while (cnt < sprom_size_words) { 41*e7ec2e32SMichael Buesch memcpy(tmp, dump, 4); 42*e7ec2e32SMichael Buesch dump += 4; 43*e7ec2e32SMichael Buesch parsed = simple_strtoul(tmp, NULL, 16); 44*e7ec2e32SMichael Buesch sprom[cnt++] = swab16((u16)parsed); 45*e7ec2e32SMichael Buesch } 46*e7ec2e32SMichael Buesch 47*e7ec2e32SMichael Buesch return 0; 48*e7ec2e32SMichael Buesch } 49*e7ec2e32SMichael Buesch 50*e7ec2e32SMichael Buesch /* Common sprom device-attribute show-handler */ 51*e7ec2e32SMichael Buesch ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, 52*e7ec2e32SMichael Buesch int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)) 53*e7ec2e32SMichael Buesch { 54*e7ec2e32SMichael Buesch u16 *sprom; 55*e7ec2e32SMichael Buesch int err = -ENOMEM; 56*e7ec2e32SMichael Buesch ssize_t count = 0; 57*e7ec2e32SMichael Buesch size_t sprom_size_words = bus->sprom_size; 58*e7ec2e32SMichael Buesch 59*e7ec2e32SMichael Buesch sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL); 60*e7ec2e32SMichael Buesch if (!sprom) 61*e7ec2e32SMichael Buesch goto out; 62*e7ec2e32SMichael Buesch 63*e7ec2e32SMichael Buesch /* Use interruptible locking, as the SPROM write might 64*e7ec2e32SMichael Buesch * be holding the lock for several seconds. So allow userspace 65*e7ec2e32SMichael Buesch * to cancel operation. */ 66*e7ec2e32SMichael Buesch err = -ERESTARTSYS; 67*e7ec2e32SMichael Buesch if (mutex_lock_interruptible(&bus->sprom_mutex)) 68*e7ec2e32SMichael Buesch goto out_kfree; 69*e7ec2e32SMichael Buesch err = sprom_read(bus, sprom); 70*e7ec2e32SMichael Buesch mutex_unlock(&bus->sprom_mutex); 71*e7ec2e32SMichael Buesch 72*e7ec2e32SMichael Buesch if (!err) 73*e7ec2e32SMichael Buesch count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words); 74*e7ec2e32SMichael Buesch 75*e7ec2e32SMichael Buesch out_kfree: 76*e7ec2e32SMichael Buesch kfree(sprom); 77*e7ec2e32SMichael Buesch out: 78*e7ec2e32SMichael Buesch return err ? err : count; 79*e7ec2e32SMichael Buesch } 80*e7ec2e32SMichael Buesch 81*e7ec2e32SMichael Buesch /* Common sprom device-attribute store-handler */ 82*e7ec2e32SMichael Buesch ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, 83*e7ec2e32SMichael Buesch const char *buf, size_t count, 84*e7ec2e32SMichael Buesch int (*sprom_check_crc)(const u16 *sprom, size_t size), 85*e7ec2e32SMichael Buesch int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)) 86*e7ec2e32SMichael Buesch { 87*e7ec2e32SMichael Buesch u16 *sprom; 88*e7ec2e32SMichael Buesch int res = 0, err = -ENOMEM; 89*e7ec2e32SMichael Buesch size_t sprom_size_words = bus->sprom_size; 90*e7ec2e32SMichael Buesch 91*e7ec2e32SMichael Buesch sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); 92*e7ec2e32SMichael Buesch if (!sprom) 93*e7ec2e32SMichael Buesch goto out; 94*e7ec2e32SMichael Buesch err = hex2sprom(sprom, buf, count, sprom_size_words); 95*e7ec2e32SMichael Buesch if (err) { 96*e7ec2e32SMichael Buesch err = -EINVAL; 97*e7ec2e32SMichael Buesch goto out_kfree; 98*e7ec2e32SMichael Buesch } 99*e7ec2e32SMichael Buesch err = sprom_check_crc(sprom, sprom_size_words); 100*e7ec2e32SMichael Buesch if (err) { 101*e7ec2e32SMichael Buesch err = -EINVAL; 102*e7ec2e32SMichael Buesch goto out_kfree; 103*e7ec2e32SMichael Buesch } 104*e7ec2e32SMichael Buesch 105*e7ec2e32SMichael Buesch /* Use interruptible locking, as the SPROM write might 106*e7ec2e32SMichael Buesch * be holding the lock for several seconds. So allow userspace 107*e7ec2e32SMichael Buesch * to cancel operation. */ 108*e7ec2e32SMichael Buesch err = -ERESTARTSYS; 109*e7ec2e32SMichael Buesch if (mutex_lock_interruptible(&bus->sprom_mutex)) 110*e7ec2e32SMichael Buesch goto out_kfree; 111*e7ec2e32SMichael Buesch err = ssb_devices_freeze(bus); 112*e7ec2e32SMichael Buesch if (err == -EOPNOTSUPP) { 113*e7ec2e32SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " 114*e7ec2e32SMichael Buesch "No suspend support. Is CONFIG_PM enabled?\n"); 115*e7ec2e32SMichael Buesch goto out_unlock; 116*e7ec2e32SMichael Buesch } 117*e7ec2e32SMichael Buesch if (err) { 118*e7ec2e32SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); 119*e7ec2e32SMichael Buesch goto out_unlock; 120*e7ec2e32SMichael Buesch } 121*e7ec2e32SMichael Buesch res = sprom_write(bus, sprom); 122*e7ec2e32SMichael Buesch err = ssb_devices_thaw(bus); 123*e7ec2e32SMichael Buesch if (err) 124*e7ec2e32SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); 125*e7ec2e32SMichael Buesch out_unlock: 126*e7ec2e32SMichael Buesch mutex_unlock(&bus->sprom_mutex); 127*e7ec2e32SMichael Buesch out_kfree: 128*e7ec2e32SMichael Buesch kfree(sprom); 129*e7ec2e32SMichael Buesch out: 130*e7ec2e32SMichael Buesch if (res) 131*e7ec2e32SMichael Buesch return res; 132*e7ec2e32SMichael Buesch return err ? err : count; 133*e7ec2e32SMichael Buesch } 134