| /* |
| * Copyright (c) 2013 Qualcomm Atheros, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/resource.h> |
| |
| #include <linux/console.h> |
| #include <asm/serial.h> |
| |
| #include <linux/tty.h> |
| #include <linux/time.h> |
| #include <linux/serial_core.h> |
| #include <linux/serial.h> |
| #include <linux/serial_8250.h> |
| #include <linux/miscdevice.h> |
| |
| #include <asm/delay.h> |
| |
| #include <atheros.h> |
| |
| uint32_t |
| ath_otp_read(uint32_t addr) |
| { |
| uint32_t rdata, i = 0; |
| |
| if (addr >= ATH_OTP_SIZE) |
| return 0xdeadbeef; |
| |
| rdata = ath_reg_rd(ATH_OTP_MEM_ADDRESS + addr); |
| |
| do { |
| if (i++ > 65536) |
| return 0xdeadbeef; |
| |
| udelay(1); |
| |
| rdata = ath_reg_rd(ATH_OTP_STATUS0_ADDRESS); |
| |
| } while(OTP_STATUS0_EFUSE_ACCESS_BUSY_GET(rdata) || |
| !OTP_STATUS0_EFUSE_READ_DATA_VALID_GET(rdata)); |
| |
| return (ath_reg_rd(ATH_OTP_STATUS1_ADDRESS)); |
| |
| } |
| EXPORT_SYMBOL(ath_otp_read); |
| |
| uint32_t |
| ath_otp_write(uint32_t addr, uint32_t data) |
| { |
| if (addr >= ATH_OTP_SIZE) |
| return 0xdeadbeef; |
| |
| #define ATH_OTP_MAGIC 0x10ad079 |
| |
| ath_reg_wr(ATH_OTP_LDO_CONTROL, 1); |
| |
| while ((ath_reg_rd(ATH_OTP_LDO_STATUS) & 1) == 0); |
| |
| ath_reg_wr(ATH_OTP_INTF0_ADDRESS, ATH_OTP_MAGIC); |
| |
| ath_reg_wr(ATH_OTP_MEM_ADDRESS + addr, data); |
| udelay(500); |
| |
| ath_reg_wr(ATH_OTP_LDO_CONTROL, 0); |
| |
| while ((ath_reg_rd(ATH_OTP_LDO_STATUS) & 1) != 0); |
| |
| return 0; |
| |
| } |
| EXPORT_SYMBOL(ath_otp_write); |
| |
| static ssize_t |
| ath_otp_fop_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
| { |
| unsigned int val, i, pos = (unsigned int)*ppos; |
| |
| for (i = 0; i < count; i += sizeof(val), pos += sizeof(val)) { |
| |
| val = ath_otp_read(pos); |
| |
| if (val == 0xdeadbeef) |
| break; |
| |
| if (copy_to_user(buf + i, &val, sizeof(val))) |
| return -EFAULT; |
| } |
| |
| *ppos += i; |
| return i; |
| } |
| |
| static struct file_operations ath_otp_fops = { |
| .read = ath_otp_fop_read, |
| }; |
| |
| static struct miscdevice ath_otp_miscdev = { |
| ATH_OTP_MINOR, |
| "otp", |
| &ath_otp_fops |
| }; |
| |
| |
| static int __init |
| ath_otp_init(void) |
| { |
| uint32_t rdata, tdata; |
| |
| /* |
| * Calculate how many nanoseconds does a ref clock cycle take |
| * 40MHz -> 25 ns |
| * 25MHz -> 40 ns |
| */ |
| tdata = 1000 / (ath_ref_freq / 1000000); |
| |
| /* |
| * Set OTP registers: |
| * Set OTP_INTF2_PG_STROBE_PW_REG_V |
| * OTP_INTF2_PG_STROBE_PW_REG_V * |
| * (how many nanoseconds does a ref clock cycle take) |
| * = 5000 ns |
| * 40MHz -> 25 ns --> 5000 / 25 = 1000 (0xC8) |
| * 25MHz -> 40 ns --> 5000 / 40 = 1000 (0x7D) |
| */ |
| tdata = 5000 / tdata; |
| rdata = ath_reg_rd(ATH_OTP_INTF2_ADDRESS); |
| rdata &= ~OTP_INTF2_PG_STROBE_PW_REG_V_MASK; |
| ath_reg_wr(ATH_OTP_INTF2_ADDRESS, |
| rdata | OTP_INTF2_PG_STROBE_PW_REG_V_SET(tdata)); |
| |
| /* |
| * Set OTP registers : Set OTP_INTF3_RD_STROBE_PW_REG_V to 8 |
| */ |
| rdata = ath_reg_rd(ATH_OTP_INTF3_ADDRESS); |
| rdata &= ~OTP_INTF3_RD_STROBE_PW_REG_V_MASK; |
| ath_reg_wr(ATH_OTP_INTF3_ADDRESS, |
| rdata | OTP_INTF3_RD_STROBE_PW_REG_V_SET(8)); |
| |
| /* |
| * Set OTP registers : Set OTP_PGENB_SETUP_HOLD_TIME to 3 |
| */ |
| rdata = ath_reg_rd(ATH_OTP_PGENB_SETUP_HOLD_TIME_ADDRESS); |
| rdata &= ~OTP_PGENB_SETUP_HOLD_TIME_DELAY_MASK; |
| ath_reg_wr(ATH_OTP_PGENB_SETUP_HOLD_TIME_ADDRESS, |
| rdata | OTP_PGENB_SETUP_HOLD_TIME_DELAY_SET(3)); |
| |
| printk("%s: Registering OTP ", __func__); |
| |
| if ((tdata = misc_register(&ath_otp_miscdev))) { |
| printk("failed %d\n", tdata); |
| return tdata; |
| } else { |
| printk("success\n"); |
| } |
| |
| return 0; |
| } |
| |
| late_initcall(ath_otp_init); |
| |