Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Documentation/devicetree/bindings/mmc/mmc-card.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ patternProperties:
contains:
const: fixed-partitions

nvmem-layout:
$ref: /schemas/nvmem/layouts/nvmem-layout.yaml

required:
- compatible
- reg
Expand Down Expand Up @@ -86,6 +89,23 @@ examples:
read-only;
};
};

partitions-boot2 {
nvmem-layout {
compatible = "fixed-layout";

#address-cells = <1>;
#size-cells = <1>;

mac-addr@4400 {
reg = <0x4400 0x6>;
};

bd-addr@5400 {
reg = <0x5400 0x6>;
};
};
};
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,14 @@ properties:
description:
boot firmware is incorrectly passing the address in big-endian order

nvmem-cells:
maxItems: 1
description:
Nvmem data cell that contains a 6 byte BD address with the most
significant byte first (big-endian).

nvmem-cell-names:
items:
- const: local-bd-address

additionalProperties: true
10 changes: 10 additions & 0 deletions Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ properties:

ieee80211-freq-limit: true

nvmem-cells:
maxItems: 1
description:
Nvmem data cell that contains a 6 byte MAC address with the most
significant byte first (big-endian).

nvmem-cell-names:
items:
- const: mac-address

qcom,calibration-data:
$ref: /schemas/types.yaml#/definitions/uint8-array
description:
Expand Down
30 changes: 30 additions & 0 deletions arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,31 @@
no-sdio;
no-sd;

#address-cells = <1>;
#size-cells = <0>;

status = "okay";

card@0 {
compatible = "mmc-card";
reg = <0>;

partitions-boot1 {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;

wifi_mac_addr: mac-addr@4400 {
reg = <0x4400 0x6>;
};

bd_addr: bd-addr@5400 {
reg = <0x5400 0x6>;
};
};
};
};
};

&spi5 {
Expand Down Expand Up @@ -512,6 +536,9 @@
vddch0-supply = <&pm4125_l22>;
enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>;
max-speed = <3000000>;

nvmem-cells = <&bd_addr>;
nvmem-cell-names = "local-bd-address";
};
};

Expand Down Expand Up @@ -557,6 +584,9 @@
qcom,ath10k-calibration-variant = "ArduinoImola";
firmware-name = "qcm2290";

nvmem-cells = <&wifi_mac_addr>;
nvmem-cell-names = "mac-address";

status = "okay";
};

Expand Down
9 changes: 9 additions & 0 deletions block/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
by falling back to the kernel crypto API when inline
encryption hardware is not present.

config BLK_NVMEM
bool "Block device NVMEM provider"
depends on OF
depends on NVMEM
help
Allow block devices (or partitions) to act as NVMEM providers,
typically used with eMMC to store MAC addresses or Wi-Fi
calibration data on embedded devices.

source "block/partitions/Kconfig"

config BLK_PM
Expand Down
1 change: 1 addition & 0 deletions block/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
blk-crypto-sysfs.o
obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
164 changes: 164 additions & 0 deletions block/blk-nvmem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* block device NVMEM provider
*
* Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
*
* Useful on devices using a partition on an eMMC for MAC addresses or
* Wi-Fi calibration EEPROM data.
*/

#include "blk.h"
#include <linux/nvmem-provider.h>
#include <linux/of.h>
#include <linux/pagemap.h>
#include <linux/property.h>

/* List of all NVMEM devices */
static LIST_HEAD(nvmem_devices);
static DEFINE_MUTEX(devices_mutex);

struct blk_nvmem {
struct nvmem_device *nvmem;
struct device *dev;
struct list_head list;
};

static int blk_nvmem_reg_read(void *priv, unsigned int from,
void *val, size_t bytes)
{
blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
unsigned long offs = from & ~PAGE_MASK, to_read;
pgoff_t f_index = from >> PAGE_SHIFT;
struct blk_nvmem *bnv = priv;
size_t bytes_left = bytes;
struct file *bdev_file;
struct folio *folio;
void *p;
int ret = 0;

bdev_file = bdev_file_open_by_dev(bnv->dev->devt, mode, priv, NULL);
if (!bdev_file)
return -ENODEV;

if (IS_ERR(bdev_file))
return PTR_ERR(bdev_file);

while (bytes_left) {
folio = read_mapping_folio(bdev_file->f_mapping, f_index++, NULL);
if (IS_ERR(folio)) {
ret = PTR_ERR(folio);
goto err_release_bdev;
}
to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
p = folio_address(folio) + offset_in_folio(folio, offs);
memcpy(val, p, to_read);
offs = 0;
bytes_left -= to_read;
val += to_read;
folio_put(folio);
}

err_release_bdev:
fput(bdev_file);

return ret;
}

static int blk_nvmem_register(struct device *dev)
{
struct device_node *np = dev_of_node(dev);
struct block_device *bdev = dev_to_bdev(dev);
struct nvmem_config config = {};
struct blk_nvmem *bnv;

/* skip devices which do not have a device tree node */
if (!np)
return 0;

/* skip devices without an nvmem layout defined */
if (!of_get_child_by_name(np, "nvmem-layout"))
return 0;

/*
* skip block device too large to be represented as NVMEM devices
* which are using an 'int' as address
*/
if (bdev_nr_bytes(bdev) > INT_MAX)
return -EFBIG;

bnv = kzalloc_obj(*bnv);
if (!bnv)
return -ENOMEM;

config.id = NVMEM_DEVID_NONE;
config.dev = &bdev->bd_device;
config.name = dev_name(&bdev->bd_device);
config.owner = THIS_MODULE;
config.priv = bnv;
config.reg_read = blk_nvmem_reg_read;
config.size = bdev_nr_bytes(bdev);
config.word_size = 1;
config.stride = 1;
config.read_only = true;
config.root_only = true;
config.ignore_wp = true;
config.of_node = to_of_node(dev->fwnode);

bnv->dev = &bdev->bd_device;
bnv->nvmem = nvmem_register(&config);
if (IS_ERR(bnv->nvmem)) {
dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
"Failed to register NVMEM device\n");

kfree(bnv);
return PTR_ERR(bnv->nvmem);
}

mutex_lock(&devices_mutex);
list_add_tail(&bnv->list, &nvmem_devices);
mutex_unlock(&devices_mutex);

return 0;
}

static void blk_nvmem_unregister(struct device *dev)
{
struct blk_nvmem *bnv_c, *bnv = NULL;

mutex_lock(&devices_mutex);
list_for_each_entry(bnv_c, &nvmem_devices, list) {
if (bnv_c->dev == dev) {
bnv = bnv_c;
break;
}
}

if (!bnv) {
mutex_unlock(&devices_mutex);
return;
}

list_del(&bnv->list);
mutex_unlock(&devices_mutex);
nvmem_unregister(bnv->nvmem);
kfree(bnv);
}

static struct class_interface blk_nvmem_bus_interface __refdata = {
.class = &block_class,
.add_dev = &blk_nvmem_register,
.remove_dev = &blk_nvmem_unregister,
};

static int __init blk_nvmem_init(void)
{
int ret;

ret = class_interface_register(&blk_nvmem_bus_interface);
if (ret)
return ret;

return 0;
}
device_initcall(blk_nvmem_init);
5 changes: 4 additions & 1 deletion drivers/bluetooth/btqca.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
}

bda = (struct hci_rp_read_bd_addr *)skb->data;
if (!bacmp(&bda->bdaddr, &config->bdaddr))
if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
}

kfree_skb(skb);

Expand Down
18 changes: 18 additions & 0 deletions include/net/bluetooth/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,24 @@ enum {
*/
HCI_QUIRK_BDADDR_PROPERTY_BROKEN,

/* When this quirk is set, the public Bluetooth address
* initially reported by HCI Read BD Address command
* is considered invalid. The public BD Address can be
* retrieved via a 'local-bd-address' NVMEM cell.
*
* This quirk can be set before hci_register_dev is called or
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_USE_BDADDR_NVMEM,

/* When this quirk is set, the Bluetooth Device Address provided by
* the 'local-bd-address' NVMEM is stored in big-endian order.
*
* This quirk can be set before hci_register_dev is called or
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_BDADDR_NVMEM_BE,

/* When this quirk is set, the duplicate filtering during
* scanning is based on Bluetooth devices addresses. To allow
* RSSI based updates, restart scanning if needed.
Expand Down
Loading