Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2004-2011 Atheros Communications Inc.
0003  * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
0004  *
0005  * Permission to use, copy, modify, and/or distribute this software for any
0006  * purpose with or without fee is hereby granted, provided that the above
0007  * copyright notice and this permission notice appear in all copies.
0008  *
0009  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0010  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0011  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0012  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0013  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0014  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0015  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0016  */
0017 
0018 #include "core.h"
0019 #include "hif-ops.h"
0020 #include "target.h"
0021 #include "debug.h"
0022 
0023 int ath6kl_bmi_done(struct ath6kl *ar)
0024 {
0025     int ret;
0026     u32 cid = BMI_DONE;
0027 
0028     if (ar->bmi.done_sent) {
0029         ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
0030         return 0;
0031     }
0032 
0033     ar->bmi.done_sent = true;
0034 
0035     ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
0036     if (ret) {
0037         ath6kl_err("Unable to send bmi done: %d\n", ret);
0038         return ret;
0039     }
0040 
0041     return 0;
0042 }
0043 
0044 int ath6kl_bmi_get_target_info(struct ath6kl *ar,
0045                    struct ath6kl_bmi_target_info *targ_info)
0046 {
0047     int ret;
0048     u32 cid = BMI_GET_TARGET_INFO;
0049 
0050     if (ar->bmi.done_sent) {
0051         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0052         return -EACCES;
0053     }
0054 
0055     ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
0056     if (ret) {
0057         ath6kl_err("Unable to send get target info: %d\n", ret);
0058         return ret;
0059     }
0060 
0061     if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
0062         ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
0063                       sizeof(*targ_info));
0064     } else {
0065         ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
0066                 sizeof(targ_info->version));
0067     }
0068 
0069     if (ret) {
0070         ath6kl_err("Unable to recv target info: %d\n", ret);
0071         return ret;
0072     }
0073 
0074     if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
0075         /* Determine how many bytes are in the Target's targ_info */
0076         ret = ath6kl_hif_bmi_read(ar,
0077                    (u8 *)&targ_info->byte_count,
0078                    sizeof(targ_info->byte_count));
0079         if (ret) {
0080             ath6kl_err("unable to read target info byte count: %d\n",
0081                    ret);
0082             return ret;
0083         }
0084 
0085         /*
0086          * The target's targ_info doesn't match the host's targ_info.
0087          * We need to do some backwards compatibility to make this work.
0088          */
0089         if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
0090             WARN_ON(1);
0091             return -EINVAL;
0092         }
0093 
0094         /* Read the remainder of the targ_info */
0095         ret = ath6kl_hif_bmi_read(ar,
0096                    ((u8 *)targ_info) +
0097                    sizeof(targ_info->byte_count),
0098                    sizeof(*targ_info) -
0099                    sizeof(targ_info->byte_count));
0100 
0101         if (ret) {
0102             ath6kl_err("Unable to read target info (%d bytes): %d\n",
0103                    targ_info->byte_count, ret);
0104             return ret;
0105         }
0106     }
0107 
0108     ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
0109            targ_info->version, targ_info->type);
0110 
0111     return 0;
0112 }
0113 
0114 int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
0115 {
0116     u32 cid = BMI_READ_MEMORY;
0117     int ret;
0118     u32 offset;
0119     u32 len_remain, rx_len;
0120     u16 size;
0121 
0122     if (ar->bmi.done_sent) {
0123         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0124         return -EACCES;
0125     }
0126 
0127     size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
0128     if (size > ar->bmi.max_cmd_size) {
0129         WARN_ON(1);
0130         return -EINVAL;
0131     }
0132     memset(ar->bmi.cmd_buf, 0, size);
0133 
0134     ath6kl_dbg(ATH6KL_DBG_BMI,
0135            "bmi read memory: device: addr: 0x%x, len: %d\n",
0136            addr, len);
0137 
0138     len_remain = len;
0139 
0140     while (len_remain) {
0141         rx_len = (len_remain < ar->bmi.max_data_size) ?
0142                     len_remain : ar->bmi.max_data_size;
0143         offset = 0;
0144         memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0145         offset += sizeof(cid);
0146         memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0147         offset += sizeof(addr);
0148         memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
0149         offset += sizeof(len);
0150 
0151         ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0152         if (ret) {
0153             ath6kl_err("Unable to write to the device: %d\n",
0154                    ret);
0155             return ret;
0156         }
0157         ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
0158         if (ret) {
0159             ath6kl_err("Unable to read from the device: %d\n",
0160                    ret);
0161             return ret;
0162         }
0163         memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
0164         len_remain -= rx_len; addr += rx_len;
0165     }
0166 
0167     return 0;
0168 }
0169 
0170 int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
0171 {
0172     u32 cid = BMI_WRITE_MEMORY;
0173     int ret;
0174     u32 offset;
0175     u32 len_remain, tx_len;
0176     const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
0177     u8 aligned_buf[400];
0178     u8 *src;
0179 
0180     if (ar->bmi.done_sent) {
0181         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0182         return -EACCES;
0183     }
0184 
0185     if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
0186         WARN_ON(1);
0187         return -EINVAL;
0188     }
0189 
0190     if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
0191         return -E2BIG;
0192 
0193     memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
0194 
0195     ath6kl_dbg(ATH6KL_DBG_BMI,
0196            "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
0197 
0198     len_remain = len;
0199     while (len_remain) {
0200         src = &buf[len - len_remain];
0201 
0202         if (len_remain < (ar->bmi.max_data_size - header)) {
0203             if (len_remain & 3) {
0204                 /* align it with 4 bytes */
0205                 len_remain = len_remain +
0206                          (4 - (len_remain & 3));
0207                 memcpy(aligned_buf, src, len_remain);
0208                 src = aligned_buf;
0209             }
0210             tx_len = len_remain;
0211         } else {
0212             tx_len = (ar->bmi.max_data_size - header);
0213         }
0214 
0215         offset = 0;
0216         memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0217         offset += sizeof(cid);
0218         memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0219         offset += sizeof(addr);
0220         memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
0221         offset += sizeof(tx_len);
0222         memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
0223         offset += tx_len;
0224 
0225         ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0226         if (ret) {
0227             ath6kl_err("Unable to write to the device: %d\n",
0228                    ret);
0229             return ret;
0230         }
0231         len_remain -= tx_len; addr += tx_len;
0232     }
0233 
0234     return 0;
0235 }
0236 
0237 int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
0238 {
0239     u32 cid = BMI_EXECUTE;
0240     int ret;
0241     u32 offset;
0242     u16 size;
0243 
0244     if (ar->bmi.done_sent) {
0245         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0246         return -EACCES;
0247     }
0248 
0249     size = sizeof(cid) + sizeof(addr) + sizeof(param);
0250     if (size > ar->bmi.max_cmd_size) {
0251         WARN_ON(1);
0252         return -EINVAL;
0253     }
0254     memset(ar->bmi.cmd_buf, 0, size);
0255 
0256     ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
0257            addr, *param);
0258 
0259     offset = 0;
0260     memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0261     offset += sizeof(cid);
0262     memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0263     offset += sizeof(addr);
0264     memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
0265     offset += sizeof(*param);
0266 
0267     ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0268     if (ret) {
0269         ath6kl_err("Unable to write to the device: %d\n", ret);
0270         return ret;
0271     }
0272 
0273     ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
0274     if (ret) {
0275         ath6kl_err("Unable to read from the device: %d\n", ret);
0276         return ret;
0277     }
0278 
0279     memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
0280 
0281     return 0;
0282 }
0283 
0284 int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
0285 {
0286     u32 cid = BMI_SET_APP_START;
0287     int ret;
0288     u32 offset;
0289     u16 size;
0290 
0291     if (ar->bmi.done_sent) {
0292         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0293         return -EACCES;
0294     }
0295 
0296     size = sizeof(cid) + sizeof(addr);
0297     if (size > ar->bmi.max_cmd_size) {
0298         WARN_ON(1);
0299         return -EINVAL;
0300     }
0301     memset(ar->bmi.cmd_buf, 0, size);
0302 
0303     ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
0304 
0305     offset = 0;
0306     memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0307     offset += sizeof(cid);
0308     memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0309     offset += sizeof(addr);
0310 
0311     ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0312     if (ret) {
0313         ath6kl_err("Unable to write to the device: %d\n", ret);
0314         return ret;
0315     }
0316 
0317     return 0;
0318 }
0319 
0320 int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
0321 {
0322     u32 cid = BMI_READ_SOC_REGISTER;
0323     int ret;
0324     u32 offset;
0325     u16 size;
0326 
0327     if (ar->bmi.done_sent) {
0328         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0329         return -EACCES;
0330     }
0331 
0332     size = sizeof(cid) + sizeof(addr);
0333     if (size > ar->bmi.max_cmd_size) {
0334         WARN_ON(1);
0335         return -EINVAL;
0336     }
0337     memset(ar->bmi.cmd_buf, 0, size);
0338 
0339     ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
0340 
0341     offset = 0;
0342     memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0343     offset += sizeof(cid);
0344     memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0345     offset += sizeof(addr);
0346 
0347     ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0348     if (ret) {
0349         ath6kl_err("Unable to write to the device: %d\n", ret);
0350         return ret;
0351     }
0352 
0353     ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
0354     if (ret) {
0355         ath6kl_err("Unable to read from the device: %d\n", ret);
0356         return ret;
0357     }
0358     memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
0359 
0360     return 0;
0361 }
0362 
0363 int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
0364 {
0365     u32 cid = BMI_WRITE_SOC_REGISTER;
0366     int ret;
0367     u32 offset;
0368     u16 size;
0369 
0370     if (ar->bmi.done_sent) {
0371         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0372         return -EACCES;
0373     }
0374 
0375     size = sizeof(cid) + sizeof(addr) + sizeof(param);
0376     if (size > ar->bmi.max_cmd_size) {
0377         WARN_ON(1);
0378         return -EINVAL;
0379     }
0380     memset(ar->bmi.cmd_buf, 0, size);
0381 
0382     ath6kl_dbg(ATH6KL_DBG_BMI,
0383            "bmi write SOC reg: addr: 0x%x, param: %d\n",
0384             addr, param);
0385 
0386     offset = 0;
0387     memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0388     offset += sizeof(cid);
0389     memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0390     offset += sizeof(addr);
0391     memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
0392     offset += sizeof(param);
0393 
0394     ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0395     if (ret) {
0396         ath6kl_err("Unable to write to the device: %d\n", ret);
0397         return ret;
0398     }
0399 
0400     return 0;
0401 }
0402 
0403 int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
0404 {
0405     u32 cid = BMI_LZ_DATA;
0406     int ret;
0407     u32 offset;
0408     u32 len_remain, tx_len;
0409     const u32 header = sizeof(cid) + sizeof(len);
0410     u16 size;
0411 
0412     if (ar->bmi.done_sent) {
0413         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0414         return -EACCES;
0415     }
0416 
0417     size = ar->bmi.max_data_size + header;
0418     if (size > ar->bmi.max_cmd_size) {
0419         WARN_ON(1);
0420         return -EINVAL;
0421     }
0422     memset(ar->bmi.cmd_buf, 0, size);
0423 
0424     ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
0425            len);
0426 
0427     len_remain = len;
0428     while (len_remain) {
0429         tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
0430               len_remain : (ar->bmi.max_data_size - header);
0431 
0432         offset = 0;
0433         memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0434         offset += sizeof(cid);
0435         memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
0436         offset += sizeof(tx_len);
0437         memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
0438                tx_len);
0439         offset += tx_len;
0440 
0441         ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0442         if (ret) {
0443             ath6kl_err("Unable to write to the device: %d\n",
0444                    ret);
0445             return ret;
0446         }
0447 
0448         len_remain -= tx_len;
0449     }
0450 
0451     return 0;
0452 }
0453 
0454 int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
0455 {
0456     u32 cid = BMI_LZ_STREAM_START;
0457     int ret;
0458     u32 offset;
0459     u16 size;
0460 
0461     if (ar->bmi.done_sent) {
0462         ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
0463         return -EACCES;
0464     }
0465 
0466     size = sizeof(cid) + sizeof(addr);
0467     if (size > ar->bmi.max_cmd_size) {
0468         WARN_ON(1);
0469         return -EINVAL;
0470     }
0471     memset(ar->bmi.cmd_buf, 0, size);
0472 
0473     ath6kl_dbg(ATH6KL_DBG_BMI,
0474            "bmi LZ stream start: addr: 0x%x)\n",
0475             addr);
0476 
0477     offset = 0;
0478     memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
0479     offset += sizeof(cid);
0480     memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
0481     offset += sizeof(addr);
0482 
0483     ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
0484     if (ret) {
0485         ath6kl_err("Unable to start LZ stream to the device: %d\n",
0486                ret);
0487         return ret;
0488     }
0489 
0490     return 0;
0491 }
0492 
0493 int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
0494 {
0495     int ret;
0496     u32 last_word = 0;
0497     u32 last_word_offset = len & ~0x3;
0498     u32 unaligned_bytes = len & 0x3;
0499 
0500     ret = ath6kl_bmi_lz_stream_start(ar, addr);
0501     if (ret)
0502         return ret;
0503 
0504     if (unaligned_bytes) {
0505         /* copy the last word into a zero padded buffer */
0506         memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
0507     }
0508 
0509     ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
0510     if (ret)
0511         return ret;
0512 
0513     if (unaligned_bytes)
0514         ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
0515 
0516     if (!ret) {
0517         /* Close compressed stream and open a new (fake) one.
0518          * This serves mainly to flush Target caches. */
0519         ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
0520     }
0521     return ret;
0522 }
0523 
0524 void ath6kl_bmi_reset(struct ath6kl *ar)
0525 {
0526     ar->bmi.done_sent = false;
0527 }
0528 
0529 int ath6kl_bmi_init(struct ath6kl *ar)
0530 {
0531     if (WARN_ON(ar->bmi.max_data_size == 0))
0532         return -EINVAL;
0533 
0534     /* cmd + addr + len + data_size */
0535     ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
0536 
0537     ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL);
0538     if (!ar->bmi.cmd_buf)
0539         return -ENOMEM;
0540 
0541     return 0;
0542 }
0543 
0544 void ath6kl_bmi_cleanup(struct ath6kl *ar)
0545 {
0546     kfree(ar->bmi.cmd_buf);
0547     ar->bmi.cmd_buf = NULL;
0548 }