diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-29 11:58:27 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-29 11:58:27 -0800 |
commit | 2eff01ee2881becc9daaa0d53477ec202136b1f4 (patch) | |
tree | d90cf5527266962e3a249a1f96e10e4afe29d246 /drivers/misc | |
parent | 55cb93fd243bad2c6e15f9151a32f575d2f5371f (diff) | |
parent | 5a6c35258d10a4966f45ee48ae24a7d4dad303ce (diff) |
Merge tag 'char-misc-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc/IIO/whatever driver subsystem updates from Greg KH:
"Here is the 'big and hairy' char/misc/iio and other small driver
subsystem updates for 6.13-rc1.
Loads of things in here, and even a fun merge conflict!
- rust misc driver bindings and other rust changes to make misc
drivers actually possible.
I think this is the tipping point, expect to see way more rust
drivers going forward now that these bindings are present. Next
merge window hopefully we will have pci and platform drivers
working, which will fully enable almost all driver subsystems to
start accepting (or at least getting) rust drivers.
This is the end result of a lot of work from a lot of people,
congrats to all of them for getting this far, you've proved many of
us wrong in the best way possible, working code :)
- IIO driver updates, too many to list individually, that subsystem
keeps growing and growing...
- Interconnect driver updates
- nvmem driver updates
- pwm driver updates
- platform_driver::remove() fixups, loads of them
- counter driver updates
- misc driver updates (keba?)
- binder driver updates and fixes
- loads of other small char/misc/etc driver updates and additions,
full details in the shortlog.
All of these have been in linux-next for a while, with no other
reported issues other than that merge conflict"
* tag 'char-misc-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (401 commits)
mei: vsc: Fix typo "maintstepping" -> "mainstepping"
firmware: Switch back to struct platform_driver::remove()
misc: isl29020: Fix the wrong format specifier
scripts/tags.sh: Don't tag usages of DEFINE_MUTEX
fpga: Switch back to struct platform_driver::remove()
mei: vsc: Improve error logging in vsc_identify_silicon()
mei: vsc: Do not re-enable interrupt from vsc_tp_reset()
dt-bindings: spmi: qcom,x1e80100-spmi-pmic-arb: Add SAR2130P compatible
dt-bindings: spmi: spmi-mtk-pmif: Add compatible for MT8188
spmi: pmic-arb: fix return path in for_each_available_child_of_node()
iio: Move __private marking before struct element priv in struct iio_dev
docs: iio: ad7380: add adaq4370-4 and adaq4380-4
iio: adc: ad7380: add support for adaq4370-4 and adaq4380-4
iio: adc: ad7380: use local dev variable to shorten long lines
iio: adc: ad7380: fix oversampling formula
dt-bindings: iio: adc: ad7380: add adaq4370-4 and adaq4380-4 compatible parts
bus: mhi: host: pci_generic: Use pcim_iomap_region() to request and map MHI BAR
bus: mhi: host: Switch trace_mhi_gen_tre fields to native endian
misc: atmel-ssc: Use of_property_present() for non-boolean properties
misc: keba: Add hardware dependency
...
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/apds990x.c | 12 | ||||
-rw-r--r-- | drivers/misc/atmel-ssc.c | 4 | ||||
-rw-r--r-- | drivers/misc/cardreader/alcor_pci.c | 2 | ||||
-rw-r--r-- | drivers/misc/cardreader/rtsx_usb.c | 6 | ||||
-rw-r--r-- | drivers/misc/eeprom/Kconfig | 4 | ||||
-rw-r--r-- | drivers/misc/eeprom/eeprom_93xx46.c | 2 | ||||
-rw-r--r-- | drivers/misc/isl29020.c | 2 | ||||
-rw-r--r-- | drivers/misc/keba/Kconfig | 13 | ||||
-rw-r--r-- | drivers/misc/keba/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/keba/cp500.c | 558 | ||||
-rw-r--r-- | drivers/misc/keba/lan9252.c | 359 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/platform-vsc.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/vsc-fw-loader.c | 26 | ||||
-rw-r--r-- | drivers/misc/mei/vsc-tp.c | 2 | ||||
-rw-r--r-- | drivers/misc/rpmb-core.c | 9 | ||||
-rw-r--r-- | drivers/misc/ti-st/Kconfig | 19 | ||||
-rw-r--r-- | drivers/misc/ti-st/Makefile | 7 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_core.c | 918 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_kim.c | 839 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_ll.c | 156 |
23 files changed, 945 insertions, 2000 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 902c306bc972..09cbe3f0ab1e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -637,7 +637,6 @@ config MCHP_LAN966X_PCI source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" -source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 885b22989580..40bf953185c7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -40,7 +40,6 @@ obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o -obj-y += ti-st/ obj-y += lis3lv02d/ obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 6d4edd69db12..e7d73c972f65 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1147,7 +1147,7 @@ static int apds990x_probe(struct i2c_client *client) err = chip->pdata->setup_resources(); if (err) { err = -EINVAL; - goto fail3; + goto fail4; } } @@ -1155,7 +1155,7 @@ static int apds990x_probe(struct i2c_client *client) apds990x_attribute_group); if (err < 0) { dev_err(&chip->client->dev, "Sysfs registration failed\n"); - goto fail4; + goto fail5; } err = request_threaded_irq(client->irq, NULL, @@ -1166,15 +1166,17 @@ static int apds990x_probe(struct i2c_client *client) if (err) { dev_err(&client->dev, "could not get IRQ %d\n", client->irq); - goto fail5; + goto fail6; } return err; -fail5: +fail6: sysfs_remove_group(&chip->client->dev.kobj, &apds990x_attribute_group[0]); -fail4: +fail5: if (chip->pdata && chip->pdata->release_resources) chip->pdata->release_resources(); +fail4: + pm_runtime_disable(&client->dev); fail3: regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); fail2: diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 6eac0f335915..1d0322dfaf79 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -153,7 +153,7 @@ static int ssc_sound_dai_probe(struct ssc_device *ssc) ssc->sound_dai = false; - if (!of_property_read_bool(np, "#sound-dai-cells")) + if (!of_property_present(np, "#sound-dai-cells")) return 0; id = of_alias_get_id(np, "ssc"); @@ -176,7 +176,7 @@ static void ssc_sound_dai_remove(struct ssc_device *ssc) #else static inline int ssc_sound_dai_probe(struct ssc_device *ssc) { - if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells")) + if (of_property_present(ssc->pdev->dev.of_node, "#sound-dai-cells")) return -ENOTSUPP; return 0; diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index 0142c4bf4f42..a5549eaf52d0 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -17,8 +17,6 @@ #include <linux/alcor_pci.h> -#define DRV_NAME_ALCOR_PCI "alcor_pci" - static DEFINE_IDA(alcor_pci_idr); static struct mfd_cell alcor_pci_cells[] = { diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index f150d8769f19..77b0490a1b38 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -20,11 +20,11 @@ MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); static const struct mfd_cell rtsx_usb_cells[] = { [RTSX_USB_SD_CARD] = { - .name = "rtsx_usb_sdmmc", + .name = DRV_NAME_RTSX_USB_SDMMC, .pdata_size = 0, }, [RTSX_USB_MS_CARD] = { - .name = "rtsx_usb_ms", + .name = DRV_NAME_RTSX_USB_MS, .pdata_size = 0, }, }; @@ -780,7 +780,7 @@ static const struct usb_device_id rtsx_usb_usb_ids[] = { MODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); static struct usb_driver rtsx_usb_driver = { - .name = "rtsx_usb", + .name = DRV_NAME_RTSX_USB, .probe = rtsx_usb_probe, .disconnect = rtsx_usb_disconnect, .suspend = rtsx_usb_suspend, diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 9df12399bda3..cb1c4b8e7fd3 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -97,11 +97,11 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. config EEPROM_IDT_89HPESX - tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support" + tristate "IDT 89HPESx PCIe-switches EEPROM / CSR support" depends on I2C && SYSFS help Enable this driver to get read/write access to EEPROM / CSRs - over IDT PCIe-swtich i2c-slave interface. + over IDT PCIe-switch i2c-slave interface. This driver can also be built as a module. If so, the module will be called idt_89hpesx. diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index e2221be88445..9cae6f530679 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -229,7 +229,7 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) static ssize_t eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, - const char *buf, unsigned off) + const char *buf, unsigned int off) { struct spi_message m; struct spi_transfer t[2] = {}; diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 1643ba2ff964..c288aeec16c0 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -68,7 +68,7 @@ static ssize_t als_lux_input_data_show(struct device *dev, if (val < 0) return val; lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536; - return sprintf(buf, "%ld\n", lux); + return sprintf(buf, "%lu\n", lux); } static ssize_t als_sensing_range_store(struct device *dev, diff --git a/drivers/misc/keba/Kconfig b/drivers/misc/keba/Kconfig index 5fbcbc2252ac..d6d47197a963 100644 --- a/drivers/misc/keba/Kconfig +++ b/drivers/misc/keba/Kconfig @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 config KEBA_CP500 tristate "KEBA CP500 system FPGA support" + depends on X86_64 || ARM64 || COMPILE_TEST depends on PCI + depends on I2C select AUXILIARY_BUS help This driver supports the KEBA CP500 system FPGA, which is used in @@ -11,3 +13,14 @@ config KEBA_CP500 This driver can also be built as a module. If so, the module will be called cp500. + +config KEBA_LAN9252 + tristate "KEBA CP500 LAN9252 configuration" + depends on SPI + depends on KEBA_CP500 || COMPILE_TEST + help + This driver is used for updating the configuration of the LAN9252 + controller on KEBA CP500 devices. + + This driver can also be built as a module. If so, the module will be + called lan9252. diff --git a/drivers/misc/keba/Makefile b/drivers/misc/keba/Makefile index 0a8b846cda7d..05e9efcad54f 100644 --- a/drivers/misc/keba/Makefile +++ b/drivers/misc/keba/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_KEBA_CP500) += cp500.o +obj-$(CONFIG_KEBA_LAN9252) += lan9252.o diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index ae0922817881..255d3022dae8 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -12,7 +12,12 @@ #include <linux/i2c.h> #include <linux/misc/keba.h> #include <linux/module.h> +#include <linux/mtd/partitions.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> #include <linux/pci.h> +#include <linux/spi/flash.h> +#include <linux/spi/spi.h> #define CP500 "cp500" @@ -27,6 +32,7 @@ /* BAR 0 registers */ #define CP500_VERSION_REG 0x00 #define CP500_RECONFIG_REG 0x11 /* upper 8-bits of STARTUP register */ +#define CP500_PRESENT_REG 0x20 #define CP500_AXI_REG 0x40 /* Bits in BUILD_REG */ @@ -35,14 +41,35 @@ /* Bits in RECONFIG_REG */ #define CP500_RECFG_REQ 0x01 /* reconfigure FPGA on next reset */ +/* Bits in PRESENT_REG */ +#define CP500_PRESENT_FAN0 0x01 + /* MSIX */ #define CP500_AXI_MSIX 3 +#define CP500_RFB_UART_MSIX 4 +#define CP500_DEBUG_UART_MSIX 5 +#define CP500_SI1_UART_MSIX 6 #define CP500_NUM_MSIX 8 #define CP500_NUM_MSIX_NO_MMI 2 #define CP500_NUM_MSIX_NO_AXI 3 /* EEPROM */ -#define CP500_HW_CPU_EEPROM_NAME "cp500_cpu_eeprom" +#define CP500_EEPROM_DA_OFFSET 0x016F +#define CP500_EEPROM_DA_ESC_TYPE_MASK 0x01 +#define CP500_EEPROM_ESC_LAN9252 0x00 +#define CP500_EEPROM_ESC_ET1100 0x01 +#define CP500_EEPROM_CPU_NAME "cpu_eeprom" +#define CP500_EEPROM_CPU_OFFSET 0 +#define CP500_EEPROM_CPU_SIZE 3072 +#define CP500_EEPROM_USER_NAME "user_eeprom" +#define CP500_EEPROM_USER_OFFSET 3072 +#define CP500_EEPROM_USER_SIZE 1024 + +/* SPI flash running at full speed */ +#define CP500_FLASH_HZ (33 * 1000 * 1000) + +/* LAN9252 */ +#define CP500_LAN9252_HZ (10 * 1000 * 1000) #define CP500_IS_CP035(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP035) #define CP500_IS_CP505(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP505) @@ -51,29 +78,56 @@ struct cp500_dev_info { off_t offset; size_t size; + unsigned int msix; }; struct cp500_devs { struct cp500_dev_info startup; + struct cp500_dev_info spi; struct cp500_dev_info i2c; + struct cp500_dev_info fan; + struct cp500_dev_info batt; + struct cp500_dev_info uart0_rfb; + struct cp500_dev_info uart1_dbg; + struct cp500_dev_info uart2_si1; }; /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ static struct cp500_devs cp035_devices = { .startup = { 0x0000, SZ_4K }, + .spi = { 0x1000, SZ_4K }, .i2c = { 0x4000, SZ_4K }, + .fan = { 0x9000, SZ_4K }, + .batt = { 0xA000, SZ_4K }, + .uart0_rfb = { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 = { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ static struct cp500_devs cp505_devices = { .startup = { 0x0000, SZ_4K }, + .spi = { 0x4000, SZ_4K }, .i2c = { 0x5000, SZ_4K }, + .fan = { 0x9000, SZ_4K }, + .batt = { 0xA000, SZ_4K }, + .uart0_rfb = { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 = { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; /* list of devices within FPGA of CP520 family (CP520, CP530) */ static struct cp500_devs cp520_devices = { - .startup = { 0x0000, SZ_4K }, - .i2c = { 0x5000, SZ_4K }, + .startup = { 0x0000, SZ_4K }, + .spi = { 0x4000, SZ_4K }, + .i2c = { 0x5000, SZ_4K }, + .fan = { 0x8000, SZ_4K }, + .batt = { 0x9000, SZ_4K }, + .uart0_rfb = { 0xC000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart1_dbg = { 0xD000, SZ_4K, CP500_DEBUG_UART_MSIX }, +}; + +struct cp500_nvmem { + struct nvmem_device *nvmem; + unsigned int offset; }; struct cp500 { @@ -85,18 +139,31 @@ struct cp500 { int minor; int build; } version; + struct notifier_block nvmem_notifier; + atomic_t nvmem_notified; /* system FPGA BAR */ resource_size_t sys_hwbase; + struct keba_spi_auxdev *spi; struct keba_i2c_auxdev *i2c; + struct keba_fan_auxdev *fan; + struct keba_batt_auxdev *batt; + struct keba_uart_auxdev *uart0_rfb; + struct keba_uart_auxdev *uart1_dbg; + struct keba_uart_auxdev *uart2_si1; /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; + /* NVMEM devices */ + struct cp500_nvmem nvmem_cpu; + struct cp500_nvmem nvmem_user; + void __iomem *system_startup_addr; }; /* I2C devices */ +#define CP500_EEPROM_ADDR 0x50 static struct i2c_board_info cp500_i2c_info[] = { { /* temperature sensor */ I2C_BOARD_INFO("emc1403", 0x4c), @@ -107,30 +174,66 @@ static struct i2c_board_info cp500_i2c_info[] = { * CP505 family: bridge board * CP520 family: carrier board */ - I2C_BOARD_INFO("24c32", 0x50), - .dev_name = CP500_HW_CPU_EEPROM_NAME, + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR), }, { /* interface board EEPROM */ - I2C_BOARD_INFO("24c32", 0x51), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 1), }, { /* * EEPROM (optional) * CP505 family: CPU board * CP520 family: MMI board */ - I2C_BOARD_INFO("24c32", 0x52), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 2), }, { /* extension module 0 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x53), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 3), }, { /* extension module 1 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x54), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 4), }, { /* extension module 2 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x55), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 5), }, { /* extension module 3 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x56), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 6), + } +}; + +/* SPI devices */ +static struct mtd_partition cp500_partitions[] = { + { + .name = "system-flash-parts", + .size = MTDPART_SIZ_FULL, + .offset = 0, + .mask_flags = 0 + } +}; +static const struct flash_platform_data cp500_w25q32 = { + .type = "w25q32", + .name = "system-flash", + .parts = cp500_partitions, + .nr_parts = ARRAY_SIZE(cp500_partitions), +}; +static const struct flash_platform_data cp500_m25p16 = { + .type = "m25p16", + .name = "system-flash", + .parts = cp500_partitions, + .nr_parts = ARRAY_SIZE(cp500_partitions), +}; +static struct spi_board_info cp500_spi_info[] = { + { /* system FPGA configuration bitstream flash */ + .modalias = "m25p80", + .platform_data = &cp500_m25p16, + .max_speed_hz = CP500_FLASH_HZ, + .chip_select = 0, + .mode = SPI_MODE_3, + }, { /* LAN9252 EtherCAT slave controller */ + .modalias = "lan9252", + .platform_data = NULL, + .max_speed_hz = CP500_LAN9252_HZ, + .chip_select = 1, + .mode = SPI_MODE_3, } }; @@ -229,7 +332,7 @@ static void cp500_i2c_release(struct device *dev) static int cp500_register_i2c(struct cp500 *cp500) { - int retval; + int ret; cp500->i2c = kzalloc(sizeof(*cp500->i2c), GFP_KERNEL); if (!cp500->i2c) @@ -251,30 +354,412 @@ static int cp500_register_i2c(struct cp500 *cp500) cp500->i2c->info_size = ARRAY_SIZE(cp500_i2c_info); cp500->i2c->info = cp500_i2c_info; - retval = auxiliary_device_init(&cp500->i2c->auxdev); - if (retval) { + ret = auxiliary_device_init(&cp500->i2c->auxdev); + if (ret) { kfree(cp500->i2c); cp500->i2c = NULL; - return retval; + return ret; } - retval = __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); - if (retval) { + ret = __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); + if (ret) { auxiliary_device_uninit(&cp500->i2c->auxdev); cp500->i2c = NULL; - return retval; + return ret; + } + + return 0; +} + +static void cp500_spi_release(struct device *dev) +{ + struct keba_spi_auxdev *spi = + container_of(dev, struct keba_spi_auxdev, auxdev.dev); + + kfree(spi); +} + +static int cp500_register_spi(struct cp500 *cp500, u8 esc_type) +{ + int info_size; + int ret; + + cp500->spi = kzalloc(sizeof(*cp500->spi), GFP_KERNEL); + if (!cp500->spi) + return -ENOMEM; + + if (CP500_IS_CP035(cp500)) + cp500_spi_info[0].platform_data = &cp500_w25q32; + if (esc_type == CP500_EEPROM_ESC_LAN9252) + info_size = ARRAY_SIZE(cp500_spi_info); + else + info_size = ARRAY_SIZE(cp500_spi_info) - 1; + + cp500->spi->auxdev.name = "spi"; + cp500->spi->auxdev.id = 0; + cp500->spi->auxdev.dev.release = cp500_spi_release; + cp500->spi->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->spi->io = (struct resource) { + /* SPI register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset + + cp500->devs->spi.size - 1, + .flags = IORESOURCE_MEM, + }; + cp500->spi->info_size = info_size; + cp500->spi->info = cp500_spi_info; + + ret = auxiliary_device_init(&cp500->spi->auxdev); + if (ret) { + kfree(cp500->spi); + cp500->spi = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->spi->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->spi->auxdev); + cp500->spi = NULL; + + return ret; + } + + return 0; +} + +static void cp500_fan_release(struct device *dev) +{ + struct keba_fan_auxdev *fan = + container_of(dev, struct keba_fan_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_fan(struct cp500 *cp500) +{ + int ret; + + cp500->fan = kzalloc(sizeof(*cp500->fan), GFP_KERNEL); + if (!cp500->fan) + return -ENOMEM; + + cp500->fan->auxdev.name = "fan"; + cp500->fan->auxdev.id = 0; + cp500->fan->auxdev.dev.release = cp500_fan_release; + cp500->fan->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->fan->io = (struct resource) { + /* fan register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset + + cp500->devs->fan.size - 1, + .flags = IORESOURCE_MEM, + }; + + ret = auxiliary_device_init(&cp500->fan->auxdev); + if (ret) { + kfree(cp500->fan); + cp500->fan = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->fan->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->fan->auxdev); + cp500->fan = NULL; + + return ret; + } + + return 0; +} + +static void cp500_batt_release(struct device *dev) +{ + struct keba_batt_auxdev *fan = + container_of(dev, struct keba_batt_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_batt(struct cp500 *cp500) +{ + int ret; + + cp500->batt = kzalloc(sizeof(*cp500->batt), GFP_KERNEL); + if (!cp500->batt) + return -ENOMEM; + + cp500->batt->auxdev.name = "batt"; + cp500->batt->auxdev.id = 0; + cp500->batt->auxdev.dev.release = cp500_batt_release; + cp500->batt->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->batt->io = (struct resource) { + /* battery register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset + + cp500->devs->batt.size - 1, + .flags = IORESOURCE_MEM, + }; + + ret = auxiliary_device_init(&cp500->batt->auxdev); + if (ret) { + kfree(cp500->batt); + cp500->batt = NULL; + + return ret; + } + ret = __auxiliary_device_add(&cp500->batt->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->batt->auxdev); + cp500->batt = NULL; + + return ret; } return 0; } +static void cp500_uart_release(struct device *dev) +{ + struct keba_uart_auxdev *uart = + container_of(dev, struct keba_uart_auxdev, auxdev.dev); + + kfree(uart); +} + +static int cp500_register_uart(struct cp500 *cp500, + struct keba_uart_auxdev **uart, const char *name, + struct cp500_dev_info *info, unsigned int irq) +{ + int ret; + + *uart = kzalloc(sizeof(**uart), GFP_KERNEL); + if (!*uart) + return -ENOMEM; + + (*uart)->auxdev.name = name; + (*uart)->auxdev.id = 0; + (*uart)->auxdev.dev.release = cp500_uart_release; + (*uart)->auxdev.dev.parent = &cp500->pci_dev->dev; + (*uart)->io = (struct resource) { + /* UART register area */ + .start = (resource_size_t) cp500->sys_hwbase + info->offset, + .end = (resource_size_t) cp500->sys_hwbase + info->offset + + info->size - 1, + .flags = IORESOURCE_MEM, + }; + (*uart)->irq = irq; + + ret = auxiliary_device_init(&(*uart)->auxdev); + if (ret) { + kfree(*uart); + *uart = NULL; + + return ret; + } + ret = __auxiliary_device_add(&(*uart)->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&(*uart)->auxdev); + *uart = NULL; + + return ret; + } + + return 0; +} + +static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem = priv; + int ret; + + ret = nvmem_device_read(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret != bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem = priv; + int ret; + + ret = nvmem_device_write(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret != bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_register(struct cp500 *cp500, struct nvmem_device *nvmem) +{ + struct device *dev = &cp500->pci_dev->dev; + struct nvmem_config nvmem_config = {}; + struct nvmem_device *tmp; + + /* + * The main EEPROM of CP500 devices is logically split into two EEPROMs. + * The first logical EEPROM with 3 kB contains the type label which is + * programmed during production of the device. The second logical EEPROM + * with 1 kB is not programmed during production and can be used for + * arbitrary user data. + */ + + nvmem_config.dev = dev; + nvmem_config.owner = THIS_MODULE; + nvmem_config.id = NVMEM_DEVID_NONE; + nvmem_config.type = NVMEM_TYPE_EEPROM; + nvmem_config.root_only = true; + nvmem_config.reg_read = cp500_nvmem_read; + nvmem_config.reg_write = cp500_nvmem_write; + + cp500->nvmem_cpu.nvmem = nvmem; + cp500->nvmem_cpu.offset = CP500_EEPROM_CPU_OFFSET; + nvmem_config.name = CP500_EEPROM_CPU_NAME; + nvmem_config.size = CP500_EEPROM_CPU_SIZE; + nvmem_config.priv = &cp500->nvmem_cpu; + tmp = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + cp500->nvmem_user.nvmem = nvmem; + cp500->nvmem_user.offset = CP500_EEPROM_USER_OFFSET; + nvmem_config.name = CP500_EEPROM_USER_NAME; + nvmem_config.size = CP500_EEPROM_USER_SIZE; + nvmem_config.priv = &cp500->nvmem_user; + tmp = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + return 0; +} + +static int cp500_nvmem_match(struct device *dev, const void *data) +{ + const struct cp500 *cp500 = data; + struct i2c_client *client; + + /* match only CPU EEPROM below the cp500 device */ + dev = dev->parent; + client = i2c_verify_client(dev); + if (!client || client->addr != CP500_EEPROM_ADDR) + return 0; + while ((dev = dev->parent)) + if (dev == &cp500->pci_dev->dev) + return 1; + + return 0; +} + +static void cp500_devm_nvmem_put(void *data) +{ + struct nvmem_device *nvmem = data; + + nvmem_device_put(nvmem); +} + +static int cp500_nvmem(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct nvmem_device *nvmem; + struct cp500 *cp500; + struct device *dev; + int notified; + u8 esc_type; + int ret; + + if (action != NVMEM_ADD) + return NOTIFY_DONE; + cp500 = container_of(nb, struct cp500, nvmem_notifier); + dev = &cp500->pci_dev->dev; + + /* process CPU EEPROM content only once */ + notified = atomic_read(&cp500->nvmem_notified); + if (notified) + return NOTIFY_DONE; + nvmem = nvmem_device_find(cp500, cp500_nvmem_match); + if (IS_ERR_OR_NULL(nvmem)) + return NOTIFY_DONE; + if (!atomic_try_cmpxchg_relaxed(&cp500->nvmem_notified, ¬ified, 1)) { + nvmem_device_put(nvmem); + + return NOTIFY_DONE; + } + + ret = devm_add_action_or_reset(dev, cp500_devm_nvmem_put, nvmem); + if (ret) + return ret; + + ret = cp500_nvmem_register(cp500, nvmem); + if (ret) + return ret; + + ret = nvmem_device_read(nvmem, CP500_EEPROM_DA_OFFSET, sizeof(esc_type), + (void *)&esc_type); + if (ret != sizeof(esc_type)) { + dev_warn(dev, "Failed to read device assembly!\n"); + + return NOTIFY_DONE; + } + esc_type &= CP500_EEPROM_DA_ESC_TYPE_MASK; + + if (cp500_register_spi(cp500, esc_type)) + dev_warn(dev, "Failed to register SPI!\n"); + + return NOTIFY_OK; +} + static void cp500_register_auxiliary_devs(struct cp500 *cp500) { struct device *dev = &cp500->pci_dev->dev; + u8 present = ioread8(cp500->system_startup_addr + CP500_PRESENT_REG); if (cp500_register_i2c(cp500)) - dev_warn(dev, "Failed to register i2c!\n"); + dev_warn(dev, "Failed to register I2C!\n"); + if (present & CP500_PRESENT_FAN0) + if (cp500_register_fan(cp500)) + dev_warn(dev, "Failed to register fan!\n"); + if (cp500_register_batt(cp500)) + dev_warn(dev, "Failed to register battery!\n"); + if (cp500->devs->uart0_rfb.size && + cp500->devs->uart0_rfb.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart0_rfb.msix); + + if (cp500_register_uart(cp500, &cp500->uart0_rfb, "rs485-uart", + &cp500->devs->uart0_rfb, irq)) + dev_warn(dev, "Failed to register RFB UART!\n"); + } + if (cp500->devs->uart1_dbg.size && + cp500->devs->uart1_dbg.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart1_dbg.msix); + + if (cp500_register_uart(cp500, &cp500->uart1_dbg, "rs232-uart", + &cp500->devs->uart1_dbg, irq)) + dev_warn(dev, "Failed to register debug UART!\n"); + } + if (cp500->devs->uart2_si1.size && + cp500->devs->uart2_si1.msix < cp500->msix_num) { + int irq = pci_irq_vector(cp500->pci_dev, + cp500->devs->uart2_si1.msix); + + if (cp500_register_uart(cp500, &cp500->uart2_si1, "uart", + &cp500->devs->uart2_si1, irq)) + dev_warn(dev, "Failed to register SI1 UART!\n"); + } } static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -285,11 +770,34 @@ static void cp500_unregister_dev(struct auxiliary_device *auxdev) static void cp500_unregister_auxiliary_devs(struct cp500 *cp500) { - + if (cp500->spi) { + cp500_unregister_dev(&cp500->spi->auxdev); + cp500->spi = NULL; + } if (cp500->i2c) { cp500_unregister_dev(&cp500->i2c->auxdev); cp500->i2c = NULL; } + if (cp500->fan) { + cp500_unregister_dev(&cp500->fan->auxdev); + cp500->fan = NULL; + } + if (cp500->batt) { + cp500_unregister_dev(&cp500->batt->auxdev); + cp500->batt = NULL; + } + if (cp500->uart0_rfb) { + cp500_unregister_dev(&cp500->uart0_rfb->auxdev); + cp500->uart0_rfb = NULL; + } + if (cp500->uart1_dbg) { + cp500_unregister_dev(&cp500->uart1_dbg->auxdev); + cp500->uart1_dbg = NULL; + } + if (cp500->uart2_si1) { + cp500_unregister_dev(&cp500->uart2_si1->auxdev); + cp500->uart2_si1 = NULL; + } } static irqreturn_t cp500_axi_handler(int irq, void *dev) @@ -396,15 +904,21 @@ static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) pci_set_drvdata(pci_dev, cp500); + cp500->nvmem_notifier.notifier_call = cp500_nvmem; + ret = nvmem_register_notifier(&cp500->nvmem_notifier); + if (ret != 0) + goto out_free_irq; ret = cp500_enable(cp500); if (ret != 0) - goto out_free_irq; + goto out_unregister_nvmem; cp500_register_auxiliary_devs(cp500); return 0; +out_unregister_nvmem: + nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); out_disable: @@ -422,6 +936,8 @@ static void cp500_remove(struct pci_dev *pci_dev) cp500_disable(cp500); + nvmem_unregister_notifier(&cp500->nvmem_notifier); + pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c new file mode 100644 index 000000000000..fc54afd1d05b --- /dev/null +++ b/drivers/misc/keba/lan9252.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) KEBA Industrial Automation Gmbh 2024 + * + * Driver for LAN9252 on KEBA CP500 devices + * + * This driver is used for updating the configuration of the LAN9252 controller + * on KEBA CP500 devices. The LAN9252 is connected over SPI, which is also named + * PDI. + */ + +#include <linux/spi/spi.h> +#include <linux/mii.h> + +/* SPI commands */ +#define LAN9252_SPI_READ 0x3 +#define LAN9252_SPI_WRITE 0x2 + +struct lan9252_read_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; +} __packed; + +struct lan9252_write_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; + u32 data; +} __packed; + +/* byte test register */ +#define LAN9252_BYTE_TEST 0x64 +#define LAN9252_BYTE_TEST_VALUE 0x87654321 + +/* hardware configuration register */ +#define LAN9252_HW_CFG 0x74 +#define LAN9252_HW_CFG_READY 0x08000000 + +/* EtherCAT CSR interface data register */ +#define LAN9252_ECAT_CSR_DATA 0x300 + +/* EtherCAT CSR interface command register */ +#define LAN9252_ECAT_CSR_CMD 0x304 +#define LAN9252_ECAT_CSR_BUSY 0x80000000 +#define LAN9252_ECAT_CSR_READ 0x40000000 + +/* EtherCAT slave controller MII register */ +#define LAN9252_ESC_MII 0x510 +#define LAN9252_ESC_MII_BUSY 0x8000 +#define LAN9252_ESC_MII_CMD_ERR 0x4000 +#define LAN9252_ESC_MII_READ_ERR 0x2000 +#define LAN9252_ESC_MII_ERR_MASK (LAN9252_ESC_MII_CMD_ERR | \ + LAN9252_ESC_MII_READ_ERR) +#define LAN9252_ESC_MII_WRITE 0x0200 +#define LAN9252_ESC_MII_READ 0x0100 + +/* EtherCAT slave controller PHY address register */ +#define LAN9252_ESC_PHY_ADDR 0x512 + +/* EtherCAT slave controller PHY register address register */ +#define LAN9252_ESC_PHY_REG_ADDR 0x513 + +/* EtherCAT slave controller PHY data register */ +#define LAN9252_ESC_PHY_DATA 0x514 + +/* EtherCAT slave controller PDI access state register */ +#define LAN9252_ESC_MII_PDI 0x517 +#define LAN9252_ESC_MII_ACCESS_PDI 0x01 +#define LAN9252_ESC_MII_ACCESS_ECAT 0x00 + +/* PHY address */ +#define PHY_ADDRESS 2 + +#define SPI_RETRY_COUNT 10 +#define SPI_WAIT_US 100 +#define SPI_CSR_WAIT_US 500 + +static int lan9252_spi_read(struct spi_device *spi, u16 addr, u32 *data) +{ + struct lan9252_read_cmd cmd; + + cmd.cmd = LAN9252_SPI_READ; + cmd.addr_0 = (addr >> 8) & 0xFF; + cmd.addr_1 = addr & 0xFF; + + return spi_write_then_read(spi, (u8 *)&cmd, + sizeof(struct lan9252_read_cmd), + (u8 *)data, sizeof(u32)); +} + +static int lan9252_spi_write(struct spi_device *spi, u16 addr, u32 data) +{ + struct lan9252_write_cmd cmd; + + cmd.cmd = LAN9252_SPI_WRITE; + cmd.addr_0 = (addr >> 8) & 0xFF; + cmd.addr_1 = addr & 0xFF; + cmd.data = data; + + return spi_write(spi, (u8 *)&cmd, sizeof(struct lan9252_write_cmd)); +} + +static bool lan9252_init(struct spi_device *spi) +{ + u32 data; + int ret; + + ret = lan9252_spi_read(spi, LAN9252_BYTE_TEST, &data); + if (ret || data != LAN9252_BYTE_TEST_VALUE) + return false; + + ret = lan9252_spi_read(spi, LAN9252_HW_CFG, &data); + if (ret || !(data & LAN9252_HW_CFG_READY)) + return false; + + return true; +} + +static u8 lan9252_esc_get_size(u16 addr) +{ + if (addr == LAN9252_ESC_MII || addr == LAN9252_ESC_PHY_DATA) + return 2; + + return 1; +} + +static int lan9252_esc_wait(struct spi_device *spi) +{ + ktime_t timeout = ktime_add_us(ktime_get(), SPI_WAIT_US); + u32 data; + int ret; + + /* wait while CSR command is busy */ + for (;;) { + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + if (!(data & LAN9252_ECAT_CSR_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + break; + } + } + + return (!(data & LAN9252_ECAT_CSR_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_esc_read(struct spi_device *spi, u16 addr, u32 *data) +{ + u32 csr_cmd; + u8 size; + int ret; + + size = lan9252_esc_get_size(addr); + csr_cmd = LAN9252_ECAT_CSR_BUSY | LAN9252_ECAT_CSR_READ; + csr_cmd |= (size << 16) | addr; + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret = lan9252_esc_wait(spi); + if (ret) + return ret; + + ret = lan9252_spi_read(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + return 0; +} + +static int lan9252_esc_write(struct spi_device *spi, u16 addr, u32 data) +{ + u32 csr_cmd; + u8 size; + int ret; + + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + size = lan9252_esc_get_size(addr); + csr_cmd = LAN9252_ECAT_CSR_BUSY; + csr_cmd |= (size << 16) | addr; + ret = lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret = lan9252_esc_wait(spi); + if (ret) + return ret; + + return 0; +} + +static int lan9252_access_mii(struct spi_device *spi, bool access) +{ + u32 data; + + if (access) + data = LAN9252_ESC_MII_ACCESS_PDI; + else + data = LAN9252_ESC_MII_ACCESS_ECAT; + + return lan9252_esc_write(spi, LAN9252_ESC_MII_PDI, data); +} + +static int lan9252_mii_wait(struct spi_device *spi) +{ + ktime_t timeout = ktime_add_us(ktime_get(), SPI_CSR_WAIT_US); + u32 data; + int ret; + + /* wait while MII control state machine is busy */ + for (;;) { + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + if (!(data & LAN9252_ESC_MII_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret = lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + break; + } + } + + return (!(data & LAN9252_ESC_MII_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_mii_read(struct spi_device *spi, u8 phy_addr, u8 reg_addr, + u32 *data) +{ + int ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_READ); + if (ret) + return ret; + + ret = lan9252_mii_wait(spi); + if (ret) + return ret; + + return lan9252_esc_read(spi, LAN9252_ESC_PHY_DATA, data); +} + +static int lan9252_mii_write(struct spi_device *spi, u8 phy_addr, u8 reg_addr, + u32 data) +{ + int ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + ret = lan9252_esc_write(spi, LAN9252_ESC_PHY_DATA, data); + if (ret) + return ret; + + ret = lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_WRITE); + if (ret) + return ret; + + return lan9252_mii_wait(spi); +} + +static int lan9252_probe(struct spi_device *spi) +{ + u32 data; + int retry = SPI_RETRY_COUNT; + int ret; + + /* execute specified initialization sequence */ + while (retry && !lan9252_init(spi)) + retry--; + if (retry == 0) { + dev_err(&spi->dev, + "Can't initialize LAN9252 SPI communication!"); + return -EIO; + } + + /* enable access to MII management for PDI */ + ret = lan9252_access_mii(spi, true); + if (ret) { + dev_err(&spi->dev, "Can't enable access to MII management!"); + return ret; + } + + /* + * check PHY configuration and configure if necessary + * - full duplex + * - auto negotiation disabled + * - 100 Mbps + */ + ret = lan9252_mii_read(spi, PHY_ADDRESS, MII_BMCR, &data); + if (ret) { + dev_err(&spi->dev, "Can't read LAN9252 configuration!"); + goto out; + } + if (!(data & BMCR_FULLDPLX) || (data & BMCR_ANENABLE) || + !(data & BMCR_SPEED100)) { + /* + */ + data &= ~(BMCR_ANENABLE); + data |= (BMCR_FULLDPLX | BMCR_SPEED100); + ret = lan9252_mii_write(spi, PHY_ADDRESS, MII_BMCR, data); + if (ret) + dev_err(&spi->dev, + "Can't write LAN9252 configuration!"); + } + + dev_info(&spi->dev, "LAN9252 PHY configuration"); + +out: + /* disable access to MII management for PDI */ + lan9252_access_mii(spi, false); + + return ret; +} + +static const struct spi_device_id lan9252_id[] = { + {"lan9252"}, + {} +}; +MODULE_DEVICE_TABLE(spi, lan9252_id); + +static struct spi_driver lan9252_driver = { + .driver = { + .name = "lan9252", + }, + .probe = lan9252_probe, + .id_table = lan9252_id, +}; +module_spi_driver(lan9252_driver); + +MODULE_AUTHOR("Petar Bojanic <boja@keba.com>"); +MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); +MODULE_DESCRIPTION("KEBA LAN9252 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 5576146ab13b..718ec5d81d94 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -145,8 +145,8 @@ out: * @cl: host client * @buf: buffer to receive * @length: buffer length - * @mode: io mode * @vtag: virtual tag + * @mode: io mode * @timeout: recv timeout, 0 for infinite timeout * * Return: read size in bytes of < 0 on error diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 20a11b299bcd..71f9994da2cc 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -256,8 +256,6 @@ static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable) vsc_tp_reset(hw->tp); - vsc_tp_intr_disable(hw->tp); - return vsc_tp_init(hw->tp, mei_dev->dev); } diff --git a/drivers/misc/mei/vsc-fw-loader.c b/drivers/misc/mei/vsc-fw-loader.c index 9f129bc641f6..308b090d81bb 100644 --- a/drivers/misc/mei/vsc-fw-loader.c +++ b/drivers/misc/mei/vsc-fw-loader.c @@ -317,28 +317,34 @@ static int vsc_identify_silicon(struct vsc_fw_loader *fw_loader) cmd->data.dump_mem.addr = cpu_to_le32(VSC_EFUSE_ADDR); cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); - if (ret) - return ret; - if (ack->token == VSC_TOKEN_ERROR) - return -EINVAL; + if (ret || ack->token == VSC_TOKEN_ERROR) { + dev_err(fw_loader->dev, "CMD_DUMP_MEM error %d token %d\n", ret, ack->token); + return ret ?: -EINVAL; + } cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); cmd->cmd_id = VSC_CMD_GET_CONT; ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); - if (ret) - return ret; - if (ack->token != VSC_TOKEN_DUMP_RESP) - return -EINVAL; + if (ret || ack->token != VSC_TOKEN_DUMP_RESP) { + dev_err(fw_loader->dev, "CMD_GETCONT error %d token %d\n", ret, ack->token); + return ret ?: -EINVAL; + } version = FIELD_GET(VSC_MAINSTEPPING_VERSION_MASK, ack->payload[0]); sub_version = FIELD_GET(VSC_SUBSTEPPING_VERSION_MASK, ack->payload[0]); - if (version != VSC_MAINSTEPPING_VERSION_A) + if (version != VSC_MAINSTEPPING_VERSION_A) { + dev_err(fw_loader->dev, "mainstepping mismatch expected %d got %d\n", + VSC_MAINSTEPPING_VERSION_A, version); return -EINVAL; + } if (sub_version != VSC_SUBSTEPPING_VERSION_0 && - sub_version != VSC_SUBSTEPPING_VERSION_1) + sub_version != VSC_SUBSTEPPING_VERSION_1) { + dev_err(fw_loader->dev, "substepping %d is out of supported range %d - %d\n", + sub_version, VSC_SUBSTEPPING_VERSION_0, VSC_SUBSTEPPING_VERSION_1); return -EINVAL; + } dev_info(fw_loader->dev, "silicon stepping version is %u:%u\n", version, sub_version); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 1618cca9a731..107177b05dcd 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -364,8 +364,6 @@ void vsc_tp_reset(struct vsc_tp *tp) gpiod_set_value_cansleep(tp->wakeupfw, 1); atomic_set(&tp->assert_cnt, 0); - - enable_irq(tp->spi->irq); } EXPORT_SYMBOL_NS_GPL(vsc_tp_reset, VSC_TP); diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c index bc68cde1a8bf..2d653926cdbb 100644 --- a/drivers/misc/rpmb-core.c +++ b/drivers/misc/rpmb-core.c @@ -13,7 +13,6 @@ #include <linux/slab.h> static DEFINE_IDA(rpmb_ida); -static DEFINE_MUTEX(rpmb_mutex); /** * rpmb_dev_get() - increase rpmb device ref counter @@ -63,9 +62,7 @@ static void rpmb_dev_release(struct device *dev) { struct rpmb_dev *rdev = to_rpmb_dev(dev); - mutex_lock(&rpmb_mutex); - ida_simple_remove(&rpmb_ida, rdev->id); - mutex_unlock(&rpmb_mutex); + ida_free(&rpmb_ida, rdev->id); kfree(rdev->descr.dev_id); kfree(rdev); } @@ -175,9 +172,7 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, goto err_free_rdev; } - mutex_lock(&rpmb_mutex); - ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); - mutex_unlock(&rpmb_mutex); + ret = ida_alloc(&rpmb_ida, GFP_KERNEL); if (ret < 0) goto err_free_dev_id; rdev->id = ret; diff --git a/drivers/misc/ti-st/Kconfig b/drivers/misc/ti-st/Kconfig deleted file mode 100644 index 1503a6496f63..000000000000 --- a/drivers/misc/ti-st/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# TI's shared transport line discipline and the protocol -# drivers (BT, FM and GPS) -# -menu "Texas Instruments shared transport line discipline" -config TI_ST - tristate "Shared transport core driver" - depends on NET && TTY - depends on GPIOLIB || COMPILE_TEST - select FW_LOADER - help - This enables the shared transport core driver for TI - BT / FM and GPS combo chips. This enables protocol drivers - to register themselves with core and send data, the responses - are returned to relevant protocol drivers based on their - packet types. - -endmenu diff --git a/drivers/misc/ti-st/Makefile b/drivers/misc/ti-st/Makefile deleted file mode 100644 index 93393100952e..000000000000 --- a/drivers/misc/ti-st/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for TI's shared transport line discipline -# and its protocol drivers (BT, FM, GPS) -# -obj-$(CONFIG_TI_ST) += st_drv.o -st_drv-objs := st_core.o st_kim.o st_ll.o diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c deleted file mode 100644 index b878431553ab..000000000000 --- a/drivers/misc/ti-st/st_core.c +++ /dev/null @@ -1,918 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport Line discipline driver Core - * This hooks up ST KIM driver and ST LL driver - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy <pavan_savoy@ti.com> - */ - -#define pr_fmt(fmt) "(stc): " fmt -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/tty.h> - -#include <linux/seq_file.h> -#include <linux/skbuff.h> - -#include <linux/ti_wilink_st.h> -#include <linux/netdevice.h> - -/* - * function pointer pointing to either, - * st_kim_recv during registration to receive fw download responses - * st_int_recv after registration to receive proto stack responses - */ -static void (*st_recv)(void *disc_data, const u8 *ptr, size_t count); - -/********************************************************************/ -static void add_channel_to_table(struct st_data_s *st_gdata, - struct st_proto_s *new_proto) -{ - pr_info("%s: id %d\n", __func__, new_proto->chnl_id); - /* list now has the channel id as index itself */ - st_gdata->list[new_proto->chnl_id] = new_proto; - st_gdata->is_registered[new_proto->chnl_id] = true; -} - -static void remove_channel_from_table(struct st_data_s *st_gdata, - struct st_proto_s *proto) -{ - pr_info("%s: id %d\n", __func__, proto->chnl_id); -/* st_gdata->list[proto->chnl_id] = NULL; */ - st_gdata->is_registered[proto->chnl_id] = false; -} - -/* - * called from KIM during firmware download. - * - * This is a wrapper function to tty->ops->write_room. - * It returns number of free space available in - * uart tx buffer. - */ -int st_get_uart_wr_room(struct st_data_s *st_gdata) -{ - if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { - pr_err("tty unavailable to perform write"); - return -1; - } - - return tty_write_room(st_gdata->tty); -} - -/* - * can be called in from - * -- KIM (during fw download) - * -- ST Core (during st_write) - * - * This is the internal write function - a wrapper - * to tty->ops->write - */ -int st_int_write(struct st_data_s *st_gdata, - const unsigned char *data, int count) -{ - struct tty_struct *tty; - if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { - pr_err("tty unavailable to perform write"); - return -EINVAL; - } - tty = st_gdata->tty; -#ifdef VERBOSE - print_hex_dump(KERN_DEBUG, "<out<", DUMP_PREFIX_NONE, - 16, 1, data, count, 0); -#endif - return tty->ops->write(tty, data, count); - -} - -/* - * push the skb received to relevant - * protocol stacks - */ -static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) -{ - pr_debug(" %s(prot:%d) ", __func__, chnl_id); - - if (unlikely - (st_gdata == NULL || st_gdata->rx_skb == NULL - || st_gdata->is_registered[chnl_id] == false)) { - pr_err("chnl_id %d not registered, no data to send?", - chnl_id); - kfree_skb(st_gdata->rx_skb); - return; - } - /* - * this cannot fail - * this shouldn't take long - * - should be just skb_queue_tail for the - * protocol stack driver - */ - if (likely(st_gdata->list[chnl_id]->recv != NULL)) { - if (unlikely - (st_gdata->list[chnl_id]->recv - (st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb) - != 0)) { - pr_err(" proto stack %d's ->recv failed", chnl_id); - kfree_skb(st_gdata->rx_skb); - return; - } - } else { - pr_err(" proto stack %d's ->recv null", chnl_id); - kfree_skb(st_gdata->rx_skb); - } - return; -} - -/* - * st_reg_complete - to call registration complete callbacks - * of all protocol stack drivers - * This function is being called with spin lock held, protocol drivers are - * only expected to complete their waits and do nothing more than that. - */ -static void st_reg_complete(struct st_data_s *st_gdata, int err) -{ - unsigned char i = 0; - pr_info(" %s ", __func__); - for (i = 0; i < ST_MAX_CHANNELS; i++) { - if (likely(st_gdata != NULL && - st_gdata->is_registered[i] == true && - st_gdata->list[i]->reg_complete_cb != NULL)) { - st_gdata->list[i]->reg_complete_cb - (st_gdata->list[i]->priv_data, err); - pr_info("protocol %d's cb sent %d\n", i, err); - if (err) { /* cleanup registered protocol */ - st_gdata->is_registered[i] = false; - if (st_gdata->protos_registered) - st_gdata->protos_registered--; - } - } - } -} - -static inline int st_check_data_len(struct st_data_s *st_gdata, - unsigned char chnl_id, int len) -{ - int room = skb_tailroom(st_gdata->rx_skb); - - pr_debug("len %d room %d", len, room); - - if (!len) { - /* - * Received packet has only packet header and - * has zero length payload. So, ask ST CORE to - * forward the packet to protocol driver (BT/FM/GPS) - */ - st_send_frame(chnl_id, st_gdata); - - } else if (len > room) { - /* - * Received packet's payload length is larger. - * We can't accommodate it in created skb. - */ - pr_err("Data length is too large len %d room %d", len, - room); - kfree_skb(st_gdata->rx_skb); - } else { - /* - * Packet header has non-zero payload length and - * we have enough space in created skb. Lets read - * payload data */ - st_gdata->rx_state = ST_W4_DATA; - st_gdata->rx_count = len; - return len; - } - - /* Change ST state to continue to process next packet */ - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_skb = NULL; - st_gdata->rx_count = 0; - st_gdata->rx_chnl = 0; - - return 0; -} - -/* - * st_wakeup_ack - internal function for action when wake-up ack - * received - */ -static inline void st_wakeup_ack(struct st_data_s *st_gdata, - unsigned char cmd) -{ - struct sk_buff *waiting_skb; - unsigned long flags = 0; - - spin_lock_irqsave(&st_gdata->lock, flags); - /* - * de-Q from waitQ and Q in txQ now that the - * chip is awake - */ - while ((waiting_skb = skb_dequeue(&st_gdata->tx_waitq))) - skb_queue_tail(&st_gdata->txq, waiting_skb); - - /* state forwarded to ST LL */ - st_ll_sleep_state(st_gdata, (unsigned long)cmd); - spin_unlock_irqrestore(&st_gdata->lock, flags); - - /* wake up to send the recently copied skbs from waitQ */ - st_tx_wakeup(st_gdata); -} - -/* - * st_int_recv - ST's internal receive function. - * Decodes received RAW data and forwards to corresponding - * client drivers (Bluetooth,FM,GPS..etc). - * This can receive various types of packets, - * HCI-Events, ACL, SCO, 4 types of HCI-LL PM packets - * CH-8 packets from FM, CH-9 packets from GPS cores. - */ -static void st_int_recv(void *disc_data, const u8 *ptr, size_t count) -{ - struct st_proto_s *proto; - unsigned short payload_len = 0; - int len = 0; - unsigned char type = 0; - unsigned char *plen; - struct st_data_s *st_gdata = (struct st_data_s *)disc_data; - unsigned long flags; - - if (st_gdata == NULL) { - pr_err(" received null from TTY "); - return; - } - - pr_debug("count %zu rx_state %ld" - "rx_count %ld", count, st_gdata->rx_state, - st_gdata->rx_count); - - spin_lock_irqsave(&st_gdata->lock, flags); - /* Decode received bytes here */ - while (count) { - if (st_gdata->rx_count) { - len = min_t(unsigned int, st_gdata->rx_count, count); - skb_put_data(st_gdata->rx_skb, ptr, len); - st_gdata->rx_count -= len; - count -= len; - ptr += len; - - if (st_gdata->rx_count) - continue; - - /* Check ST RX state machine , where are we? */ - switch (st_gdata->rx_state) { - /* Waiting for complete packet ? */ - case ST_W4_DATA: - pr_debug("Complete pkt received"); - /* - * Ask ST CORE to forward - * the packet to protocol driver - */ - st_send_frame(st_gdata->rx_chnl, st_gdata); - - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_skb = NULL; - continue; - /* parse the header to know details */ - case ST_W4_HEADER: - proto = st_gdata->list[st_gdata->rx_chnl]; - plen = - &st_gdata->rx_skb->data - [proto->offset_len_in_hdr]; - pr_debug("plen pointing to %x\n", *plen); - if (proto->len_size == 1) /* 1 byte len field */ - payload_len = *(unsigned char *)plen; - else if (proto->len_size == 2) - payload_len = - __le16_to_cpu(*(unsigned short *)plen); - else - pr_info("%s: invalid length " - "for id %d\n", - __func__, proto->chnl_id); - st_check_data_len(st_gdata, proto->chnl_id, - payload_len); - pr_debug("off %d, pay len %d\n", - proto->offset_len_in_hdr, payload_len); - continue; - } /* end of switch rx_state */ - } - - /* end of if rx_count */ - - /* - * Check first byte of packet and identify module - * owner (BT/FM/GPS) - */ - switch (*ptr) { - case LL_SLEEP_IND: - case LL_SLEEP_ACK: - case LL_WAKE_UP_IND: - pr_debug("PM packet"); - /* - * this takes appropriate action based on - * sleep state received -- - */ - st_ll_sleep_state(st_gdata, *ptr); - /* - * if WAKEUP_IND collides copy from waitq to txq - * and assume chip awake - */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - if (st_ll_getstate(st_gdata) == ST_LL_AWAKE) - st_wakeup_ack(st_gdata, LL_WAKE_UP_ACK); - spin_lock_irqsave(&st_gdata->lock, flags); - - ptr++; - count--; - continue; - case LL_WAKE_UP_ACK: - pr_debug("PM packet"); - - spin_unlock_irqrestore(&st_gdata->lock, flags); - /* wake up ack received */ - st_wakeup_ack(st_gdata, *ptr); - spin_lock_irqsave(&st_gdata->lock, flags); - - ptr++; - count--; - continue; - /* Unknown packet? */ - default: - type = *ptr; - - /* - * Default case means non-HCILL packets, - * possibilities are packets for: - * (a) valid protocol - Supported Protocols within - * the ST_MAX_CHANNELS. - * (b) registered protocol - Checked by - * "st_gdata->list[type] == NULL)" are supported - * protocols only. - * Rules out any invalid protocol and - * unregistered protocols with channel ID < 16. - */ - - if ((type >= ST_MAX_CHANNELS) || - (st_gdata->list[type] == NULL)) { - pr_err("chip/interface misbehavior: " - "dropping frame starting " - "with 0x%02x\n", type); - goto done; - } - - st_gdata->rx_skb = alloc_skb( - st_gdata->list[type]->max_frame_size, - GFP_ATOMIC); - if (st_gdata->rx_skb == NULL) { - pr_err("out of memory: dropping\n"); - goto done; - } - - skb_reserve(st_gdata->rx_skb, - st_gdata->list[type]->reserve); - /* next 2 required for BT only */ - st_gdata->rx_skb->cb[0] = type; /*pkt_type*/ - st_gdata->rx_skb->cb[1] = 0; /*incoming*/ - st_gdata->rx_chnl = *ptr; - st_gdata->rx_state = ST_W4_HEADER; - st_gdata->rx_count = st_gdata->list[type]->hdr_len; - pr_debug("rx_count %ld\n", st_gdata->rx_count); - } - ptr++; - count--; - } -done: - spin_unlock_irqrestore(&st_gdata->lock, flags); - pr_debug("done %s", __func__); - return; -} - -/* - * st_int_dequeue - internal de-Q function. - * If the previous data set was not written - * completely, return that skb which has the pending data. - * In normal cases, return top of txq. - */ -static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) -{ - struct sk_buff *returning_skb; - - pr_debug("%s", __func__); - if (st_gdata->tx_skb != NULL) { - returning_skb = st_gdata->tx_skb; - st_gdata->tx_skb = NULL; - return returning_skb; - } - return skb_dequeue(&st_gdata->txq); -} - -/* - * st_int_enqueue - internal Q-ing function. - * Will either Q the skb to txq or the tx_waitq - * depending on the ST LL state. - * If the chip is asleep, then Q it onto waitq and - * wakeup the chip. - * txq and waitq needs protection since the other contexts - * may be sending data, waking up chip. - */ -static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) -{ - unsigned long flags = 0; - - pr_debug("%s", __func__); - spin_lock_irqsave(&st_gdata->lock, flags); - - switch (st_ll_getstate(st_gdata)) { - case ST_LL_AWAKE: - pr_debug("ST LL is AWAKE, sending normally"); - skb_queue_tail(&st_gdata->txq, skb); - break; - case ST_LL_ASLEEP_TO_AWAKE: - skb_queue_tail(&st_gdata->tx_waitq, skb); - break; - case ST_LL_AWAKE_TO_ASLEEP: - pr_err("ST LL is illegal state(%ld)," - "purging received skb.", st_ll_getstate(st_gdata)); - dev_kfree_skb_irq(skb); - break; - case ST_LL_ASLEEP: - skb_queue_tail(&st_gdata->tx_waitq, skb); - st_ll_wakeup(st_gdata); - break; - default: - pr_err("ST LL is illegal state(%ld)," - "purging received skb.", st_ll_getstate(st_gdata)); - dev_kfree_skb_irq(skb); - break; - } - - spin_unlock_irqrestore(&st_gdata->lock, flags); - pr_debug("done %s", __func__); - return; -} - -/* - * internal wakeup function - * called from either - * - TTY layer when write's finished - * - st_write (in context of the protocol stack) - */ -static void work_fn_write_wakeup(struct work_struct *work) -{ - struct st_data_s *st_gdata = container_of(work, struct st_data_s, - work_write_wakeup); - - st_tx_wakeup((void *)st_gdata); -} -void st_tx_wakeup(struct st_data_s *st_data) -{ - struct sk_buff *skb; - unsigned long flags; /* for irq save flags */ - pr_debug("%s", __func__); - /* check for sending & set flag sending here */ - if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) { - pr_debug("ST already sending"); - /* keep sending */ - set_bit(ST_TX_WAKEUP, &st_data->tx_state); - return; - /* TX_WAKEUP will be checked in another - * context - */ - } - do { /* come back if st_tx_wakeup is set */ - /* woke-up to write */ - clear_bit(ST_TX_WAKEUP, &st_data->tx_state); - while ((skb = st_int_dequeue(st_data))) { - int len; - spin_lock_irqsave(&st_data->lock, flags); - /* enable wake-up from TTY */ - set_bit(TTY_DO_WRITE_WAKEUP, &st_data->tty->flags); - len = st_int_write(st_data, skb->data, skb->len); - skb_pull(skb, len); - /* if skb->len = len as expected, skb->len=0 */ - if (skb->len) { - /* would be the next skb to be sent */ - st_data->tx_skb = skb; - spin_unlock_irqrestore(&st_data->lock, flags); - break; - } - dev_kfree_skb_irq(skb); - spin_unlock_irqrestore(&st_data->lock, flags); - } - /* if wake-up is set in another context- restart sending */ - } while (test_bit(ST_TX_WAKEUP, &st_data->tx_state)); - - /* clear flag sending */ - clear_bit(ST_TX_SENDING, &st_data->tx_state); -} - -/********************************************************************/ -/* functions called from ST KIM -*/ -void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf) -{ - seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n", - st_gdata->protos_registered, - st_gdata->is_registered[0x04] == true ? 'R' : 'U', - st_gdata->is_registered[0x08] == true ? 'R' : 'U', - st_gdata->is_registered[0x09] == true ? 'R' : 'U'); -} - -/********************************************************************/ -/* - * functions called from protocol stack drivers - * to be EXPORT-ed - */ -long st_register(struct st_proto_s *new_proto) -{ - struct st_data_s *st_gdata; - long err = 0; - unsigned long flags = 0; - - st_kim_ref(&st_gdata, 0); - if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL - || new_proto->reg_complete_cb == NULL) { - pr_err("gdata/new_proto/recv or reg_complete_cb not ready"); - return -EINVAL; - } - - if (new_proto->chnl_id >= ST_MAX_CHANNELS) { - pr_err("chnl_id %d not supported", new_proto->chnl_id); - return -EPROTONOSUPPORT; - } - - if (st_gdata->is_registered[new_proto->chnl_id] == true) { - pr_err("chnl_id %d already registered", new_proto->chnl_id); - return -EALREADY; - } - - /* can be from process context only */ - spin_lock_irqsave(&st_gdata->lock, flags); - - if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { - pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id); - /* fw download in progress */ - - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - - set_bit(ST_REG_PENDING, &st_gdata->st_state); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EINPROGRESS; - } else if (st_gdata->protos_registered == ST_EMPTY) { - pr_info(" chnl_id list empty :%d ", new_proto->chnl_id); - set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - st_recv = st_kim_recv; - - /* enable the ST LL - to set default chip state */ - st_ll_enable(st_gdata); - - /* release lock previously held - re-locked below */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - - /* - * this may take a while to complete - * since it involves BT fw download - */ - err = st_kim_start(st_gdata->kim_data); - if (err != 0) { - clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - if ((st_gdata->protos_registered != ST_EMPTY) && - (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_err(" KIM failure complete callback "); - spin_lock_irqsave(&st_gdata->lock, flags); - st_reg_complete(st_gdata, err); - spin_unlock_irqrestore(&st_gdata->lock, flags); - clear_bit(ST_REG_PENDING, &st_gdata->st_state); - } - return -EINVAL; - } - - spin_lock_irqsave(&st_gdata->lock, flags); - - clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); - st_recv = st_int_recv; - - /* - * this is where all pending registration - * are signalled to be complete by calling callback functions - */ - if ((st_gdata->protos_registered != ST_EMPTY) && - (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_debug(" call reg complete callback "); - st_reg_complete(st_gdata, 0); - } - clear_bit(ST_REG_PENDING, &st_gdata->st_state); - - /* - * check for already registered once more, - * since the above check is old - */ - if (st_gdata->is_registered[new_proto->chnl_id] == true) { - pr_err(" proto %d already registered ", - new_proto->chnl_id); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EALREADY; - } - - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - spin_unlock_irqrestore(&st_gdata->lock, flags); - return err; - } - /* if fw is already downloaded & new stack registers protocol */ - else { - add_channel_to_table(st_gdata, new_proto); - st_gdata->protos_registered++; - new_proto->write = st_write; - - /* lock already held before entering else */ - spin_unlock_irqrestore(&st_gdata->lock, flags); - return err; - } -} -EXPORT_SYMBOL_GPL(st_register); - -/* - * to unregister a protocol - - * to be called from protocol stack driver - */ -long st_unregister(struct st_proto_s *proto) -{ - long err = 0; - unsigned long flags = 0; - struct st_data_s *st_gdata; - - pr_debug("%s: %d ", __func__, proto->chnl_id); - - st_kim_ref(&st_gdata, 0); - if (!st_gdata || proto->chnl_id >= ST_MAX_CHANNELS) { - pr_err(" chnl_id %d not supported", proto->chnl_id); - return -EPROTONOSUPPORT; - } - - spin_lock_irqsave(&st_gdata->lock, flags); - - if (st_gdata->is_registered[proto->chnl_id] == false) { - pr_err(" chnl_id %d not registered", proto->chnl_id); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EPROTONOSUPPORT; - } - - if (st_gdata->protos_registered) - st_gdata->protos_registered--; - - remove_channel_from_table(st_gdata, proto); - spin_unlock_irqrestore(&st_gdata->lock, flags); - - if ((st_gdata->protos_registered == ST_EMPTY) && - (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_info(" all chnl_ids unregistered "); - - /* stop traffic on tty */ - if (st_gdata->tty) { - tty_ldisc_flush(st_gdata->tty); - stop_tty(st_gdata->tty); - } - - /* all chnl_ids now unregistered */ - st_kim_stop(st_gdata->kim_data); - /* disable ST LL */ - st_ll_disable(st_gdata); - } - return err; -} - -/* - * called in protocol stack drivers - * via the write function pointer - */ -long st_write(struct sk_buff *skb) -{ - struct st_data_s *st_gdata; - long len; - - st_kim_ref(&st_gdata, 0); - if (unlikely(skb == NULL || st_gdata == NULL - || st_gdata->tty == NULL)) { - pr_err("data/tty unavailable to perform write"); - return -EINVAL; - } - - pr_debug("%d to be written", skb->len); - len = skb->len; - - /* st_ll to decide where to enqueue the skb */ - st_int_enqueue(st_gdata, skb); - /* wake up */ - st_tx_wakeup(st_gdata); - - /* return number of bytes written */ - return len; -} - -/* for protocols making use of shared transport */ -EXPORT_SYMBOL_GPL(st_unregister); - -/********************************************************************/ -/* - * functions called from TTY layer - */ -static int st_tty_open(struct tty_struct *tty) -{ - struct st_data_s *st_gdata; - pr_info("%s ", __func__); - - st_kim_ref(&st_gdata, 0); - st_gdata->tty = tty; - tty->disc_data = st_gdata; - - /* don't do an wakeup for now */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - /* mem already allocated - */ - tty->receive_room = 65536; - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - /* - * signal to UIM via KIM that - - * installation of N_TI_WL ldisc is complete - */ - st_kim_complete(st_gdata->kim_data); - pr_debug("done %s", __func__); - - return 0; -} - -static void st_tty_close(struct tty_struct *tty) -{ - unsigned char i; - unsigned long flags; - struct st_data_s *st_gdata = tty->disc_data; - - pr_info("%s ", __func__); - - /* - * TODO: - * if a protocol has been registered & line discipline - * un-installed for some reason - what should be done ? - */ - spin_lock_irqsave(&st_gdata->lock, flags); - for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { - if (st_gdata->is_registered[i] == true) - pr_err("%d not un-registered", i); - st_gdata->list[i] = NULL; - st_gdata->is_registered[i] = false; - } - st_gdata->protos_registered = 0; - spin_unlock_irqrestore(&st_gdata->lock, flags); - /* - * signal to UIM via KIM that - - * N_TI_WL ldisc is un-installed - */ - st_kim_complete(st_gdata->kim_data); - st_gdata->tty = NULL; - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - - spin_lock_irqsave(&st_gdata->lock, flags); - /* empty out txq and tx_waitq */ - skb_queue_purge(&st_gdata->txq); - skb_queue_purge(&st_gdata->tx_waitq); - /* reset the TTY Rx states of ST */ - st_gdata->rx_count = 0; - st_gdata->rx_state = ST_W4_PACKET_TYPE; - kfree_skb(st_gdata->rx_skb); - st_gdata->rx_skb = NULL; - spin_unlock_irqrestore(&st_gdata->lock, flags); - - pr_debug("%s: done ", __func__); -} - -static void st_tty_receive(struct tty_struct *tty, const u8 *data, - const u8 *tty_flags, size_t count) -{ -#ifdef VERBOSE - print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE, - 16, 1, data, count, 0); -#endif - - /* - * if fw download is in progress then route incoming data - * to KIM for validation - */ - st_recv(tty->disc_data, data, count); - pr_debug("done %s", __func__); -} - -/* - * wake-up function called in from the TTY layer - * inside the internal wakeup function will be called - */ -static void st_tty_wakeup(struct tty_struct *tty) -{ - struct st_data_s *st_gdata = tty->disc_data; - pr_debug("%s ", __func__); - /* don't do an wakeup for now */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - /* - * schedule the internal wakeup instead of calling directly to - * avoid lockup (port->lock needed in tty->ops->write is - * already taken here - */ - schedule_work(&st_gdata->work_write_wakeup); -} - -static void st_tty_flush_buffer(struct tty_struct *tty) -{ - struct st_data_s *st_gdata = tty->disc_data; - pr_debug("%s ", __func__); - - kfree_skb(st_gdata->tx_skb); - st_gdata->tx_skb = NULL; - - tty_driver_flush_buffer(tty); - return; -} - -static struct tty_ldisc_ops st_ldisc_ops = { - .num = N_TI_WL, - .name = "n_st", - .open = st_tty_open, - .close = st_tty_close, - .receive_buf = st_tty_receive, - .write_wakeup = st_tty_wakeup, - .flush_buffer = st_tty_flush_buffer, - .owner = THIS_MODULE -}; - -/********************************************************************/ -int st_core_init(struct st_data_s **core_data) -{ - struct st_data_s *st_gdata; - long err; - - err = tty_register_ldisc(&st_ldisc_ops); - if (err) { - pr_err("error registering %d line discipline %ld", - N_TI_WL, err); - return err; - } - pr_debug("registered n_shared line discipline"); - - st_gdata = kzalloc(sizeof(struct st_data_s), GFP_KERNEL); - if (!st_gdata) { - pr_err("memory allocation failed"); - err = -ENOMEM; - goto err_unreg_ldisc; - } - - /* Initialize ST TxQ and Tx waitQ queue head. All BT/FM/GPS module skb's - * will be pushed in this queue for actual transmission. - */ - skb_queue_head_init(&st_gdata->txq); - skb_queue_head_init(&st_gdata->tx_waitq); - - /* Locking used in st_int_enqueue() to avoid multiple execution */ - spin_lock_init(&st_gdata->lock); - - err = st_ll_init(st_gdata); - if (err) { - pr_err("error during st_ll initialization(%ld)", err); - goto err_free_gdata; - } - - INIT_WORK(&st_gdata->work_write_wakeup, work_fn_write_wakeup); - - *core_data = st_gdata; - return 0; -err_free_gdata: - kfree(st_gdata); -err_unreg_ldisc: - tty_unregister_ldisc(&st_ldisc_ops); - return err; -} - -void st_core_exit(struct st_data_s *st_gdata) -{ - long err; - /* internal module cleanup */ - err = st_ll_deinit(st_gdata); - if (err) - pr_err("error during deinit of ST LL %ld", err); - - if (st_gdata != NULL) { - /* Free ST Tx Qs and skbs */ - skb_queue_purge(&st_gdata->txq); - skb_queue_purge(&st_gdata->tx_waitq); - kfree_skb(st_gdata->rx_skb); - kfree_skb(st_gdata->tx_skb); - /* TTY ldisc cleanup */ - tty_unregister_ldisc(&st_ldisc_ops); - /* free the global data pointer */ - kfree(st_gdata); - } -} diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c deleted file mode 100644 index ff172cf4614d..000000000000 --- a/drivers/misc/ti-st/st_kim.c +++ /dev/null @@ -1,839 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport Line discipline driver Core - * Init Manager module responsible for GPIO control - * and firmware download - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy <pavan_savoy@ti.com> - */ - -#define pr_fmt(fmt) "(stk) :" fmt -#include <linux/platform_device.h> -#include <linux/jiffies.h> -#include <linux/firmware.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/gpio.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/sched.h> -#include <linux/sysfs.h> -#include <linux/tty.h> - -#include <linux/skbuff.h> -#include <linux/ti_wilink_st.h> -#include <linux/module.h> - -#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ -static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; - -/**********************************************************************/ -/* internal functions */ - -/* - * st_get_plat_device - - * function which returns the reference to the platform device - * requested by id. As of now only 1 such device exists (id=0) - * the context requesting for reference can get the id to be - * requested by a. The protocol driver which is registering or - * b. the tty device which is opened. - */ -static struct platform_device *st_get_plat_device(int id) -{ - return st_kim_devices[id]; -} - -/* - * validate_firmware_response - - * function to return whether the firmware response was proper - * in case of error don't complete so that waiting for proper - * response times out - */ -static void validate_firmware_response(struct kim_data_s *kim_gdata) -{ - struct sk_buff *skb = kim_gdata->rx_skb; - if (!skb) - return; - - /* - * these magic numbers are the position in the response buffer which - * allows us to distinguish whether the response is for the read - * version info. command - */ - if (skb->data[2] == 0x01 && skb->data[3] == 0x01 && - skb->data[4] == 0x10 && skb->data[5] == 0x00) { - /* fw version response */ - memcpy(kim_gdata->resp_buffer, - kim_gdata->rx_skb->data, - kim_gdata->rx_skb->len); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - kim_gdata->rx_count = 0; - } else if (unlikely(skb->data[5] != 0)) { - pr_err("no proper response during fw download"); - pr_err("data6 %x", skb->data[5]); - kfree_skb(skb); - return; /* keep waiting for the proper response */ - } - /* becos of all the script being downloaded */ - complete_all(&kim_gdata->kim_rcvd); - kfree_skb(skb); -} - -/* - * check for data len received inside kim_int_recv - * most often hit the last case to update state to waiting for data - */ -static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) -{ - register int room = skb_tailroom(kim_gdata->rx_skb); - - pr_debug("len %d room %d", len, room); - - if (!len) { - validate_firmware_response(kim_gdata); - } else if (len > room) { - /* - * Received packet's payload length is larger. - * We can't accommodate it in created skb. - */ - pr_err("Data length is too large len %d room %d", len, - room); - kfree_skb(kim_gdata->rx_skb); - } else { - /* - * Packet header has non-zero payload length and - * we have enough space in created skb. Lets read - * payload data */ - kim_gdata->rx_state = ST_W4_DATA; - kim_gdata->rx_count = len; - return len; - } - - /* - * Change ST LL state to continue to process next - * packet - */ - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - kim_gdata->rx_count = 0; - - return 0; -} - -/* - * kim_int_recv - receive function called during firmware download - * firmware download responses on different UART drivers - * have been observed to come in bursts of different - * tty_receive and hence the logic - */ -static void kim_int_recv(struct kim_data_s *kim_gdata, const u8 *ptr, - size_t count) -{ - int len = 0; - unsigned char *plen; - - pr_debug("%s", __func__); - /* Decode received bytes here */ - while (count) { - if (kim_gdata->rx_count) { - len = min_t(unsigned int, kim_gdata->rx_count, count); - skb_put_data(kim_gdata->rx_skb, ptr, len); - kim_gdata->rx_count -= len; - count -= len; - ptr += len; - - if (kim_gdata->rx_count) - continue; - - /* Check ST RX state machine , where are we? */ - switch (kim_gdata->rx_state) { - /* Waiting for complete packet ? */ - case ST_W4_DATA: - pr_debug("Complete pkt received"); - validate_firmware_response(kim_gdata); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_skb = NULL; - continue; - /* Waiting for Bluetooth event header ? */ - case ST_W4_HEADER: - plen = - (unsigned char *)&kim_gdata->rx_skb->data[1]; - pr_debug("event hdr: plen 0x%02x\n", *plen); - kim_check_data_len(kim_gdata, *plen); - continue; - } /* end of switch */ - } /* end of if rx_state */ - switch (*ptr) { - /* Bluetooth event packet? */ - case 0x04: - kim_gdata->rx_state = ST_W4_HEADER; - kim_gdata->rx_count = 2; - break; - default: - pr_info("unknown packet"); - ptr++; - count--; - continue; - } - ptr++; - count--; - kim_gdata->rx_skb = - alloc_skb(1024+8, GFP_ATOMIC); - if (!kim_gdata->rx_skb) { - pr_err("can't allocate mem for new packet"); - kim_gdata->rx_state = ST_W4_PACKET_TYPE; - kim_gdata->rx_count = 0; - return; - } - skb_reserve(kim_gdata->rx_skb, 8); - kim_gdata->rx_skb->cb[0] = 4; - kim_gdata->rx_skb->cb[1] = 0; - - } - return; -} - -static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) -{ - unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0; - static const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 }; - long time_left; - - pr_debug("%s", __func__); - - reinit_completion(&kim_gdata->kim_rcvd); - if (4 != st_int_write(kim_gdata->core_data, read_ver_cmd, 4)) { - pr_err("kim: couldn't write 4 bytes"); - return -EIO; - } - - time_left = wait_for_completion_interruptible_timeout( - &kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME)); - if (time_left <= 0) { - pr_err(" waiting for ver info- timed out or received signal"); - return time_left ? -ERESTARTSYS : -ETIMEDOUT; - } - reinit_completion(&kim_gdata->kim_rcvd); - /* - * the positions 12 & 13 in the response buffer provide with the - * chip, major & minor numbers - */ - - version = - MAKEWORD(kim_gdata->resp_buffer[12], - kim_gdata->resp_buffer[13]); - chip = (version & 0x7C00) >> 10; - min_ver = (version & 0x007F); - maj_ver = (version & 0x0380) >> 7; - - if (version & 0x8000) - maj_ver |= 0x0008; - - sprintf(bts_scr_name, "ti-connectivity/TIInit_%d.%d.%d.bts", - chip, maj_ver, min_ver); - - /* to be accessed later via sysfs entry */ - kim_gdata->version.full = version; - kim_gdata->version.chip = chip; - kim_gdata->version.maj_ver = maj_ver; - kim_gdata->version.min_ver = min_ver; - - pr_info("%s", bts_scr_name); - return 0; -} - -static void skip_change_remote_baud(unsigned char **ptr, long *len) -{ - unsigned char *nxt_action, *cur_action; - cur_action = *ptr; - - nxt_action = cur_action + sizeof(struct bts_action) + - ((struct bts_action *) cur_action)->size; - - if (((struct bts_action *) nxt_action)->type != ACTION_WAIT_EVENT) { - pr_err("invalid action after change remote baud command"); - } else { - *ptr = *ptr + sizeof(struct bts_action) + - ((struct bts_action *)cur_action)->size; - *len = *len - (sizeof(struct bts_action) + - ((struct bts_action *)cur_action)->size); - /* warn user on not commenting these in firmware */ - pr_warn("skipping the wait event of change remote baud"); - } -} - -/* - * download_firmware - - * internal function which parses through the .bts firmware - * script file intreprets SEND, DELAY actions only as of now - */ -static long download_firmware(struct kim_data_s *kim_gdata) -{ - long err = 0; - long len = 0; - unsigned char *ptr = NULL; - unsigned char *action_ptr = NULL; - unsigned char bts_scr_name[40] = { 0 }; /* 40 char long bts scr name? */ - int wr_room_space; - int cmd_size; - unsigned long timeout; - - err = read_local_version(kim_gdata, bts_scr_name); - if (err != 0) { - pr_err("kim: failed to read local ver"); - return err; - } - err = - request_firmware(&kim_gdata->fw_entry, bts_scr_name, - &kim_gdata->kim_pdev->dev); - if (unlikely((err != 0) || (kim_gdata->fw_entry->data == NULL) || - (kim_gdata->fw_entry->size == 0))) { - pr_err(" request_firmware failed(errno %ld) for %s", err, - bts_scr_name); - return -EINVAL; - } - ptr = (void *)kim_gdata->fw_entry->data; - len = kim_gdata->fw_entry->size; - /* - * bts_header to remove out magic number and - * version - */ - ptr += sizeof(struct bts_header); - len -= sizeof(struct bts_header); - - while (len > 0 && ptr) { - pr_debug(" action size %d, type %d ", - ((struct bts_action *)ptr)->size, - ((struct bts_action *)ptr)->type); - - switch (((struct bts_action *)ptr)->type) { - case ACTION_SEND_COMMAND: /* action send */ - pr_debug("S"); - action_ptr = &(((struct bts_action *)ptr)->data[0]); - if (unlikely - (((struct hci_command *)action_ptr)->opcode == - 0xFF36)) { - /* - * ignore remote change - * baud rate HCI VS command - */ - pr_warn("change remote baud" - " rate command in firmware"); - skip_change_remote_baud(&ptr, &len); - break; - } - /* - * Make sure we have enough free space in uart - * tx buffer to write current firmware command - */ - cmd_size = ((struct bts_action *)ptr)->size; - timeout = jiffies + msecs_to_jiffies(CMD_WR_TIME); - do { - wr_room_space = - st_get_uart_wr_room(kim_gdata->core_data); - if (wr_room_space < 0) { - pr_err("Unable to get free " - "space info from uart tx buffer"); - release_firmware(kim_gdata->fw_entry); - return wr_room_space; - } - mdelay(1); /* wait 1ms before checking room */ - } while ((wr_room_space < cmd_size) && - time_before(jiffies, timeout)); - - /* Timeout happened ? */ - if (time_after_eq(jiffies, timeout)) { - pr_err("Timeout while waiting for free " - "free space in uart tx buffer"); - release_firmware(kim_gdata->fw_entry); - return -ETIMEDOUT; - } - /* - * reinit completion before sending for the - * relevant wait - */ - reinit_completion(&kim_gdata->kim_rcvd); - - /* - * Free space found in uart buffer, call st_int_write - * to send current firmware command to the uart tx - * buffer. - */ - err = st_int_write(kim_gdata->core_data, - ((struct bts_action_send *)action_ptr)->data, - ((struct bts_action *)ptr)->size); - if (unlikely(err < 0)) { - release_firmware(kim_gdata->fw_entry); - return err; - } - /* - * Check number of bytes written to the uart tx buffer - * and requested command write size - */ - if (err != cmd_size) { - pr_err("Number of bytes written to uart " - "tx buffer are not matching with " - "requested cmd write size"); - release_firmware(kim_gdata->fw_entry); - return -EIO; - } - break; - case ACTION_WAIT_EVENT: /* wait */ - pr_debug("W"); - err = wait_for_completion_interruptible_timeout( - &kim_gdata->kim_rcvd, - msecs_to_jiffies(CMD_RESP_TIME)); - if (err <= 0) { - pr_err("response timeout/signaled during fw download "); - /* timed out */ - release_firmware(kim_gdata->fw_entry); - return err ? -ERESTARTSYS : -ETIMEDOUT; - } - reinit_completion(&kim_gdata->kim_rcvd); - break; - case ACTION_DELAY: /* sleep */ - pr_info("sleep command in scr"); - action_ptr = &(((struct bts_action *)ptr)->data[0]); - mdelay(((struct bts_action_delay *)action_ptr)->msec); - break; - } - len = - len - (sizeof(struct bts_action) + - ((struct bts_action *)ptr)->size); - ptr = - ptr + sizeof(struct bts_action) + - ((struct bts_action *)ptr)->size; - } - /* fw download complete */ - release_firmware(kim_gdata->fw_entry); - return 0; -} - -/**********************************************************************/ -/* functions called from ST core */ -/* called from ST Core, when REG_IN_PROGRESS (registration in progress) - * can be because of - * 1. response to read local version - * 2. during send/recv's of firmware download - */ -void st_kim_recv(void *disc_data, const u8 *data, size_t count) -{ - struct st_data_s *st_gdata = (struct st_data_s *)disc_data; - struct kim_data_s *kim_gdata = st_gdata->kim_data; - - /* - * proceed to gather all data and distinguish read fw version response - * from other fw responses when data gathering is complete - */ - kim_int_recv(kim_gdata, data, count); - return; -} - -/* - * to signal completion of line discipline installation - * called from ST Core, upon tty_open - */ -void st_kim_complete(void *kim_data) -{ - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - complete(&kim_gdata->ldisc_installed); -} - -/* - * st_kim_start - called from ST Core upon 1st registration - * This involves toggling the chip enable gpio, reading - * the firmware version from chip, forming the fw file name - * based on the chip version, requesting the fw, parsing it - * and perform download(send/recv). - */ -long st_kim_start(void *kim_data) -{ - long err = 0; - long retry = POR_RETRY_COUNT; - struct ti_st_plat_data *pdata; - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - - pr_info(" %s", __func__); - pdata = kim_gdata->kim_pdev->dev.platform_data; - - do { - /* platform specific enabling code here */ - if (pdata->chip_enable) - pdata->chip_enable(kim_gdata); - - /* Configure BT nShutdown to HIGH state */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - mdelay(5); /* FIXME: a proper toggle */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); - mdelay(100); - /* re-initialize the completion */ - reinit_completion(&kim_gdata->ldisc_installed); - /* send notification to UIM */ - kim_gdata->ldisc_install = 1; - pr_info("ldisc_install = 1"); - sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, - NULL, "install"); - /* wait for ldisc to be installed */ - err = wait_for_completion_interruptible_timeout( - &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); - if (!err) { - /* - * ldisc installation timeout, - * flush uart, power cycle BT_EN - */ - pr_err("ldisc installation timeout"); - err = st_kim_stop(kim_gdata); - continue; - } else { - /* ldisc installed now */ - pr_info("line discipline installed"); - err = download_firmware(kim_gdata); - if (err != 0) { - /* - * ldisc installed but fw download failed, - * flush uart & power cycle BT_EN - */ - pr_err("download firmware failed"); - err = st_kim_stop(kim_gdata); - continue; - } else { /* on success don't retry */ - break; - } - } - } while (retry--); - return err; -} - -/* - * st_kim_stop - stop communication with chip. - * This can be called from ST Core/KIM, on the- - * (a) last un-register when chip need not be powered there-after, - * (b) upon failure to either install ldisc or download firmware. - * The function is responsible to (a) notify UIM about un-installation, - * (b) flush UART if the ldisc was installed. - * (c) reset BT_EN - pull down nshutdown at the end. - * (d) invoke platform's chip disabling routine. - */ -long st_kim_stop(void *kim_data) -{ - long err = 0; - struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - struct ti_st_plat_data *pdata = - kim_gdata->kim_pdev->dev.platform_data; - struct tty_struct *tty = kim_gdata->core_data->tty; - - reinit_completion(&kim_gdata->ldisc_installed); - - if (tty) { /* can be called before ldisc is installed */ - /* Flush any pending characters in the driver and discipline. */ - tty_ldisc_flush(tty); - tty_driver_flush_buffer(tty); - } - - /* send uninstall notification to UIM */ - pr_info("ldisc_install = 0"); - kim_gdata->ldisc_install = 0; - sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install"); - - /* wait for ldisc to be un-installed */ - err = wait_for_completion_interruptible_timeout( - &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); - if (!err) { /* timeout */ - pr_err(" timed out waiting for ldisc to be un-installed"); - err = -ETIMEDOUT; - } - - /* By default configure BT nShutdown to LOW state */ - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - mdelay(1); - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); - mdelay(1); - gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); - - /* platform specific disable */ - if (pdata->chip_disable) - pdata->chip_disable(kim_gdata); - return err; -} - -/**********************************************************************/ -/* functions called from subsystems */ -/* called when debugfs entry is read from */ - -static int version_show(struct seq_file *s, void *unused) -{ - struct kim_data_s *kim_gdata = s->private; - seq_printf(s, "%04X %d.%d.%d\n", kim_gdata->version.full, - kim_gdata->version.chip, kim_gdata->version.maj_ver, - kim_gdata->version.min_ver); - return 0; -} - -static int list_show(struct seq_file *s, void *unused) -{ - struct kim_data_s *kim_gdata = s->private; - kim_st_list_protocols(kim_gdata->core_data, s); - return 0; -} - -static ssize_t show_install(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->ldisc_install); -} - -#ifdef DEBUG -static ssize_t store_dev_name(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - pr_debug("storing dev name >%s<", buf); - strscpy(kim_data->dev_name, buf, sizeof(kim_data->dev_name)); - pr_debug("stored dev name >%s<", kim_data->dev_name); - return count; -} - -static ssize_t store_baud_rate(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - pr_debug("storing baud rate >%s<", buf); - sscanf(buf, "%ld", &kim_data->baud_rate); - pr_debug("stored baud rate >%ld<", kim_data->baud_rate); - return count; -} -#endif /* if DEBUG */ - -static ssize_t show_dev_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", kim_data->dev_name); -} - -static ssize_t show_baud_rate(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->baud_rate); -} - -static ssize_t show_flow_cntrl(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kim_data_s *kim_data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", kim_data->flow_cntrl); -} - -/* structures specific for sysfs entries */ -static struct kobj_attribute ldisc_install = -__ATTR(install, 0444, (void *)show_install, NULL); - -static struct kobj_attribute uart_dev_name = -#ifdef DEBUG /* TODO: move this to debug-fs if possible */ -__ATTR(dev_name, 0644, (void *)show_dev_name, (void *)store_dev_name); -#else -__ATTR(dev_name, 0444, (void *)show_dev_name, NULL); -#endif - -static struct kobj_attribute uart_baud_rate = -#ifdef DEBUG /* TODO: move to debugfs */ -__ATTR(baud_rate, 0644, (void *)show_baud_rate, (void *)store_baud_rate); -#else -__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL); -#endif - -static struct kobj_attribute uart_flow_cntrl = -__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL); - -static struct attribute *uim_attrs[] = { - &ldisc_install.attr, - &uart_dev_name.attr, - &uart_baud_rate.attr, - &uart_flow_cntrl.attr, - NULL, -}; - -static const struct attribute_group uim_attr_grp = { - .attrs = uim_attrs, -}; - -/* - * st_kim_ref - reference the core's data - * This references the per-ST platform device in the arch/xx/ - * board-xx.c file. - * This would enable multiple such platform devices to exist - * on a given platform - */ -void st_kim_ref(struct st_data_s **core_data, int id) -{ - struct platform_device *pdev; - struct kim_data_s *kim_gdata; - /* get kim_gdata reference from platform device */ - pdev = st_get_plat_device(id); - if (!pdev) - goto err; - kim_gdata = platform_get_drvdata(pdev); - if (!kim_gdata) - goto err; - - *core_data = kim_gdata->core_data; - return; -err: - *core_data = NULL; -} - -DEFINE_SHOW_ATTRIBUTE(version); -DEFINE_SHOW_ATTRIBUTE(list); - -/**********************************************************************/ -/* functions called from platform device driver subsystem - * need to have a relevant platform device entry in the platform's - * board-*.c file - */ - -static struct dentry *kim_debugfs_dir; -static int kim_probe(struct platform_device *pdev) -{ - struct kim_data_s *kim_gdata; - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - int err; - - if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { - /* multiple devices could exist */ - st_kim_devices[pdev->id] = pdev; - } else { - /* platform's sure about existence of 1 device */ - st_kim_devices[0] = pdev; - } - - kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_KERNEL); - if (!kim_gdata) { - pr_err("no mem to allocate"); - return -ENOMEM; - } - platform_set_drvdata(pdev, kim_gdata); - - err = st_core_init(&kim_gdata->core_data); - if (err != 0) { - pr_err(" ST core init failed"); - err = -EIO; - goto err_core_init; - } - /* refer to itself */ - kim_gdata->core_data->kim_data = kim_gdata; - - /* Claim the chip enable nShutdown gpio from the system */ - kim_gdata->nshutdown = pdata->nshutdown_gpio; - err = gpio_request(kim_gdata->nshutdown, "kim"); - if (unlikely(err)) { - pr_err(" gpio %d request failed ", kim_gdata->nshutdown); - goto err_sysfs_group; - } - - /* Configure nShutdown GPIO as output=0 */ - err = gpio_direction_output(kim_gdata->nshutdown, 0); - if (unlikely(err)) { - pr_err(" unable to configure gpio %d", kim_gdata->nshutdown); - goto err_sysfs_group; - } - /* get reference of pdev for request_firmware */ - kim_gdata->kim_pdev = pdev; - init_completion(&kim_gdata->kim_rcvd); - init_completion(&kim_gdata->ldisc_installed); - - err = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp); - if (err) { - pr_err("failed to create sysfs entries"); - goto err_sysfs_group; - } - - /* copying platform data */ - strscpy(kim_gdata->dev_name, pdata->dev_name, - sizeof(kim_gdata->dev_name)); - kim_gdata->flow_cntrl = pdata->flow_cntrl; - kim_gdata->baud_rate = pdata->baud_rate; - pr_info("sysfs entries created\n"); - - kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); - - debugfs_create_file("version", S_IRUGO, kim_debugfs_dir, - kim_gdata, &version_fops); - debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, - kim_gdata, &list_fops); - return 0; - -err_sysfs_group: - st_core_exit(kim_gdata->core_data); - -err_core_init: - kfree(kim_gdata); - - return err; -} - -static void kim_remove(struct platform_device *pdev) -{ - /* free the GPIOs requested */ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - struct kim_data_s *kim_gdata; - - kim_gdata = platform_get_drvdata(pdev); - - /* - * Free the Bluetooth/FM/GPIO - * nShutdown gpio from the system - */ - gpio_free(pdata->nshutdown_gpio); - pr_info("nshutdown GPIO Freed"); - - debugfs_remove_recursive(kim_debugfs_dir); - sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); - pr_info("sysfs entries removed"); - - kim_gdata->kim_pdev = NULL; - st_core_exit(kim_gdata->core_data); - - kfree(kim_gdata); - kim_gdata = NULL; -} - -static int kim_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - - if (pdata->suspend) - return pdata->suspend(pdev, state); - - return 0; -} - -static int kim_resume(struct platform_device *pdev) -{ - struct ti_st_plat_data *pdata = pdev->dev.platform_data; - - if (pdata->resume) - return pdata->resume(pdev); - - return 0; -} - -/**********************************************************************/ -/* entry point for ST KIM module, called in from ST Core */ -static struct platform_driver kim_platform_driver = { - .probe = kim_probe, - .remove_new = kim_remove, - .suspend = kim_suspend, - .resume = kim_resume, - .driver = { - .name = "kim", - }, -}; - -module_platform_driver(kim_platform_driver); - -MODULE_AUTHOR("Pavan Savoy <pavan_savoy@ti.com>"); -MODULE_DESCRIPTION("Shared Transport Driver for TI BT/FM/GPS combo chips "); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c deleted file mode 100644 index 07406140d277..000000000000 --- a/drivers/misc/ti-st/st_ll.c +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Shared Transport driver - * HCI-LL module responsible for TI proprietary HCI_LL protocol - * Copyright (C) 2009-2010 Texas Instruments - * Author: Pavan Savoy <pavan_savoy@ti.com> - */ - -#define pr_fmt(fmt) "(stll) :" fmt -#include <linux/skbuff.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/ti_wilink_st.h> - -/**********************************************************************/ -/* internal functions */ -static void send_ll_cmd(struct st_data_s *st_data, - unsigned char cmd) -{ - - pr_debug("%s: writing %x", __func__, cmd); - st_int_write(st_data, &cmd, 1); - return; -} - -static void ll_device_want_to_sleep(struct st_data_s *st_data) -{ - struct kim_data_s *kim_data; - struct ti_st_plat_data *pdata; - - pr_debug("%s", __func__); - /* sanity check */ - if (st_data->ll_state != ST_LL_AWAKE) - pr_err("ERR hcill: ST_LL_GO_TO_SLEEP_IND" - "in state %ld", st_data->ll_state); - - send_ll_cmd(st_data, LL_SLEEP_ACK); - /* update state */ - st_data->ll_state = ST_LL_ASLEEP; - - /* communicate to platform about chip asleep */ - kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; - if (pdata->chip_asleep) - pdata->chip_asleep(NULL); -} - -static void ll_device_want_to_wakeup(struct st_data_s *st_data) -{ - struct kim_data_s *kim_data; - struct ti_st_plat_data *pdata; - - /* diff actions in diff states */ - switch (st_data->ll_state) { - case ST_LL_ASLEEP: - send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */ - break; - case ST_LL_ASLEEP_TO_AWAKE: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind while waiting for Wake ack"); - break; - case ST_LL_AWAKE: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind already AWAKE"); - break; - case ST_LL_AWAKE_TO_ASLEEP: - /* duplicate wake_ind */ - pr_err("duplicate wake_ind"); - break; - } - /* update state */ - st_data->ll_state = ST_LL_AWAKE; - - /* communicate to platform about chip wakeup */ - kim_data = st_data->kim_data; - pdata = kim_data->kim_pdev->dev.platform_data; - if (pdata->chip_awake) - pdata->chip_awake(NULL); -} - -/**********************************************************************/ -/* functions invoked by ST Core */ - -/* called when ST Core wants to - * enable ST LL */ -void st_ll_enable(struct st_data_s *ll) -{ - ll->ll_state = ST_LL_AWAKE; -} - -/* called when ST Core /local module wants to - * disable ST LL */ -void st_ll_disable(struct st_data_s *ll) -{ - ll->ll_state = ST_LL_INVALID; -} - -/* called when ST Core wants to update the state */ -void st_ll_wakeup(struct st_data_s *ll) -{ - if (likely(ll->ll_state != ST_LL_AWAKE)) { - send_ll_cmd(ll, LL_WAKE_UP_IND); /* WAKE_IND */ - ll->ll_state = ST_LL_ASLEEP_TO_AWAKE; - } else { - /* don't send the duplicate wake_indication */ - pr_err(" Chip already AWAKE "); - } -} - -/* called when ST Core wants the state */ -unsigned long st_ll_getstate(struct st_data_s *ll) -{ - pr_debug(" returning state %ld", ll->ll_state); - return ll->ll_state; -} - -/* called from ST Core, when a PM related packet arrives */ -unsigned long st_ll_sleep_state(struct st_data_s *st_data, - unsigned char cmd) -{ - switch (cmd) { - case LL_SLEEP_IND: /* sleep ind */ - pr_debug("sleep indication recvd"); - ll_device_want_to_sleep(st_data); - break; - case LL_SLEEP_ACK: /* sleep ack */ - pr_err("sleep ack rcvd: host shouldn't"); - break; - case LL_WAKE_UP_IND: /* wake ind */ - pr_debug("wake indication recvd"); - ll_device_want_to_wakeup(st_data); - break; - case LL_WAKE_UP_ACK: /* wake ack */ - pr_debug("wake ack rcvd"); - st_data->ll_state = ST_LL_AWAKE; - break; - default: - pr_err(" unknown input/state "); - return -EINVAL; - } - return 0; -} - -/* Called from ST CORE to initialize ST LL */ -long st_ll_init(struct st_data_s *ll) -{ - /* set state to invalid */ - ll->ll_state = ST_LL_INVALID; - return 0; -} - -/* Called from ST CORE to de-initialize ST LL */ -long st_ll_deinit(struct st_data_s *ll) -{ - return 0; -} |