Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: ISC
0002 /* Initialize Owl Emulation Devices
0003  *
0004  * Copyright (C) 2016 Christian Lamparter <chunkeey@gmail.com>
0005  * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
0006  *
0007  * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
0008  * need to be able to initialize the PCIe wifi device. Normally, this is done
0009  * during the early stages as a pci quirk.
0010  * However, this isn't possible for devices which have the init code for the
0011  * Atheros chip stored on UBI Volume on NAND. Hence, this module can be used to
0012  * initialize the chip when the user-space is ready to extract the init code.
0013  */
0014 #include <linux/module.h>
0015 #include <linux/completion.h>
0016 #include <linux/etherdevice.h>
0017 #include <linux/firmware.h>
0018 #include <linux/pci.h>
0019 #include <linux/delay.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/ath9k_platform.h>
0022 #include <linux/nvmem-consumer.h>
0023 #include <linux/workqueue.h>
0024 
0025 struct owl_ctx {
0026     struct pci_dev *pdev;
0027     struct completion eeprom_load;
0028     struct work_struct work;
0029     struct nvmem_cell *cell;
0030 };
0031 
0032 #define EEPROM_FILENAME_LEN 100
0033 
0034 #define AR5416_EEPROM_MAGIC 0xa55a
0035 
0036 static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
0037                size_t cal_len)
0038 {
0039     void __iomem *mem;
0040     const void *cal_end = (void *)cal_data + cal_len;
0041     const struct {
0042         u16 reg;
0043         u16 low_val;
0044         u16 high_val;
0045     } __packed * data;
0046     u16 cmd;
0047     u32 bar0;
0048     bool swap_needed = false;
0049 
0050     /* also note that we are doing *u16 operations on the file */
0051     if (cal_len > 4096 || cal_len < 0x200 || (cal_len & 1) == 1) {
0052         dev_err(&pdev->dev, "eeprom has an invalid size.\n");
0053         return -EINVAL;
0054     }
0055 
0056     if (*cal_data != AR5416_EEPROM_MAGIC) {
0057         if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
0058             dev_err(&pdev->dev, "invalid calibration data\n");
0059             return -EINVAL;
0060         }
0061 
0062         dev_dbg(&pdev->dev, "calibration data needs swapping\n");
0063         swap_needed = true;
0064     }
0065 
0066     dev_info(&pdev->dev, "fixup device configuration\n");
0067 
0068     mem = pcim_iomap(pdev, 0, 0);
0069     if (!mem) {
0070         dev_err(&pdev->dev, "ioremap error\n");
0071         return -EINVAL;
0072     }
0073 
0074     pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
0075     pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
0076                    pci_resource_start(pdev, 0));
0077     pci_read_config_word(pdev, PCI_COMMAND, &cmd);
0078     cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
0079     pci_write_config_word(pdev, PCI_COMMAND, cmd);
0080 
0081     /* set pointer to first reg address */
0082     for (data = (const void *)(cal_data + 3);
0083          (const void *)data <= cal_end && data->reg != (u16)~0;
0084          data++) {
0085         u32 val;
0086         u16 reg;
0087 
0088         reg = data->reg;
0089         val = data->low_val;
0090         val |= ((u32)data->high_val) << 16;
0091 
0092         if (swap_needed) {
0093             reg = swab16(reg);
0094             val = swahb32(val);
0095         }
0096 
0097         iowrite32(val, mem + reg);
0098         usleep_range(100, 120);
0099     }
0100 
0101     pci_read_config_word(pdev, PCI_COMMAND, &cmd);
0102     cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
0103     pci_write_config_word(pdev, PCI_COMMAND, cmd);
0104 
0105     pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
0106     pcim_iounmap(pdev, mem);
0107 
0108     pci_disable_device(pdev);
0109 
0110     return 0;
0111 }
0112 
0113 static void owl_rescan(struct pci_dev *pdev)
0114 {
0115     struct pci_bus *bus = pdev->bus;
0116 
0117     pci_lock_rescan_remove();
0118     pci_stop_and_remove_bus_device(pdev);
0119     /* the device should come back with the proper
0120      * ProductId. But we have to initiate a rescan.
0121      */
0122     pci_rescan_bus(bus);
0123     pci_unlock_rescan_remove();
0124 }
0125 
0126 static void owl_fw_cb(const struct firmware *fw, void *context)
0127 {
0128     struct owl_ctx *ctx = (struct owl_ctx *)context;
0129 
0130     complete(&ctx->eeprom_load);
0131 
0132     if (fw) {
0133         ath9k_pci_fixup(ctx->pdev, (const u16 *)fw->data, fw->size);
0134         owl_rescan(ctx->pdev);
0135     } else {
0136         dev_err(&ctx->pdev->dev, "no eeprom data received.\n");
0137     }
0138     release_firmware(fw);
0139 }
0140 
0141 static const char *owl_get_eeprom_name(struct pci_dev *pdev)
0142 {
0143     struct device *dev = &pdev->dev;
0144     char *eeprom_name;
0145 
0146     dev_dbg(dev, "using auto-generated eeprom filename\n");
0147 
0148     eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
0149     if (!eeprom_name)
0150         return NULL;
0151 
0152     /* this should match the pattern used in ath9k/init.c */
0153     scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
0154           dev_name(dev));
0155 
0156     return eeprom_name;
0157 }
0158 
0159 static void owl_nvmem_work(struct work_struct *work)
0160 {
0161     struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
0162     void *buf;
0163     size_t len;
0164 
0165     complete(&ctx->eeprom_load);
0166 
0167     buf = nvmem_cell_read(ctx->cell, &len);
0168     if (!IS_ERR(buf)) {
0169         ath9k_pci_fixup(ctx->pdev, buf, len);
0170         kfree(buf);
0171         owl_rescan(ctx->pdev);
0172     } else {
0173         dev_err(&ctx->pdev->dev, "no nvmem data received.\n");
0174     }
0175 }
0176 
0177 static int owl_nvmem_probe(struct owl_ctx *ctx)
0178 {
0179     int err;
0180 
0181     ctx->cell = devm_nvmem_cell_get(&ctx->pdev->dev, "calibration");
0182     if (IS_ERR(ctx->cell)) {
0183         err = PTR_ERR(ctx->cell);
0184         if (err == -ENOENT || err == -EOPNOTSUPP)
0185             return 1; /* not present, try firmware_request */
0186 
0187         return err;
0188     }
0189 
0190     INIT_WORK(&ctx->work, owl_nvmem_work);
0191     schedule_work(&ctx->work);
0192 
0193     return 0;
0194 }
0195 
0196 static int owl_probe(struct pci_dev *pdev,
0197              const struct pci_device_id *id)
0198 {
0199     struct owl_ctx *ctx;
0200     const char *eeprom_name;
0201     int err = 0;
0202 
0203     if (pcim_enable_device(pdev))
0204         return -EIO;
0205 
0206     pcim_pin_device(pdev);
0207 
0208     ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
0209     if (!ctx)
0210         return -ENOMEM;
0211 
0212     init_completion(&ctx->eeprom_load);
0213     ctx->pdev = pdev;
0214 
0215     pci_set_drvdata(pdev, ctx);
0216 
0217     err = owl_nvmem_probe(ctx);
0218     if (err <= 0)
0219         return err;
0220 
0221     eeprom_name = owl_get_eeprom_name(pdev);
0222     if (!eeprom_name) {
0223         dev_err(&pdev->dev, "no eeprom filename found.\n");
0224         return -ENODEV;
0225     }
0226 
0227     err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
0228                       &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
0229     if (err)
0230         dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
0231 
0232     return err;
0233 }
0234 
0235 static void owl_remove(struct pci_dev *pdev)
0236 {
0237     struct owl_ctx *ctx = pci_get_drvdata(pdev);
0238 
0239     if (ctx) {
0240         wait_for_completion(&ctx->eeprom_load);
0241         pci_set_drvdata(pdev, NULL);
0242     }
0243 }
0244 
0245 static const struct pci_device_id owl_pci_table[] = {
0246     { PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
0247     { PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
0248     { },
0249 };
0250 MODULE_DEVICE_TABLE(pci, owl_pci_table);
0251 
0252 static struct pci_driver owl_driver = {
0253     .name       = KBUILD_MODNAME,
0254     .id_table   = owl_pci_table,
0255     .probe      = owl_probe,
0256     .remove     = owl_remove,
0257 };
0258 module_pci_driver(owl_driver);
0259 MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
0260 MODULE_DESCRIPTION("External EEPROM data loader for Atheros AR500X to AR92XX");
0261 MODULE_LICENSE("Dual BSD/GPL");