Driver core patches for 3.17-rc1
Here's the big driver-core pull request for 3.17-rc1. Largest thing in here is the dma-buf rework and fence code, that touched many different subsystems so it was agreed it should go through this tree to handle merge issues. There's also some firmware loading updates, as well as tests added, and a few other tiny changes, the changelog has the details. All have been in linux-next for a long time. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlPf1XcACgkQMUfUDdst+ylREACdHLXBa02yLrRzbrONJ+nARuFv JuQAoMN49PD8K9iMQpXqKBvZBsu+iCIY =w8OJ -----END PGP SIGNATURE----- Merge tag 'driver-core-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core updates from Greg KH: "Here's the big driver-core pull request for 3.17-rc1. Largest thing in here is the dma-buf rework and fence code, that touched many different subsystems so it was agreed it should go through this tree to handle merge issues. There's also some firmware loading updates, as well as tests added, and a few other tiny changes, the changelog has the details. All have been in linux-next for a long time" * tag 'driver-core-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (32 commits) ARM: imx: Remove references to platform_bus in mxc code firmware loader: Fix _request_firmware_load() return val for fw load abort platform: Remove most references to platform_bus device test: add firmware_class loader test doc: fix minor typos in firmware_class README staging: android: Cleanup style issues Documentation: devres: Sort managed interfaces Documentation: devres: Add devm_kmalloc() et al fs: debugfs: remove trailing whitespace kernfs: kernel-doc warning fix debugfs: Fix corrupted loop in debugfs_remove_recursive stable_kernel_rules: Add pointer to netdev-FAQ for network patches driver core: platform: add device binding path 'driver_override' driver core/platform: remove unused implicit padding in platform_object firmware loader: inform direct failure when udev loader is disabled firmware: replace ALIGN(PAGE_SIZE) by PAGE_ALIGN firmware: read firmware size using i_size_read() firmware loader: allow disabling of udev as firmware loader reservation: add suppport for read-only access using rcu reservation: update api and add some helpers ... Conflicts: drivers/base/platform.c
This commit is contained in:
commit
29b88e23a9
|
@ -0,0 +1,20 @@
|
|||
What: /sys/bus/platform/devices/.../driver_override
|
||||
Date: April 2014
|
||||
Contact: Kim Phillips <kim.phillips@freescale.com>
|
||||
Description:
|
||||
This file allows the driver for a device to be specified which
|
||||
will override standard OF, ACPI, ID table, and name matching.
|
||||
When specified, only a driver with a name matching the value
|
||||
written to driver_override will have an opportunity to bind
|
||||
to the device. The override is specified by writing a string
|
||||
to the driver_override file (echo vfio-platform > \
|
||||
driver_override) and may be cleared with an empty string
|
||||
(echo > driver_override). This returns the device to standard
|
||||
matching rules binding. Writing to driver_override does not
|
||||
automatically unbind the device from its current driver or make
|
||||
any attempt to automatically load the specified driver. If no
|
||||
driver with a matching name is currently loaded in the kernel,
|
||||
the device will not bind to any driver. This also allows
|
||||
devices to opt-out of driver binding using a driver_override
|
||||
name such as "none". Only a single driver may be specified in
|
||||
the override, there is no support for parsing delimiters.
|
|
@ -128,8 +128,12 @@ X!Edrivers/base/interface.c
|
|||
!Edrivers/base/bus.c
|
||||
</sect1>
|
||||
<sect1><title>Device Drivers DMA Management</title>
|
||||
!Edrivers/base/dma-buf.c
|
||||
!Edrivers/base/reservation.c
|
||||
!Edrivers/dma-buf/dma-buf.c
|
||||
!Edrivers/dma-buf/fence.c
|
||||
!Edrivers/dma-buf/seqno-fence.c
|
||||
!Iinclude/linux/fence.h
|
||||
!Iinclude/linux/seqno-fence.h
|
||||
!Edrivers/dma-buf/reservation.c
|
||||
!Iinclude/linux/reservation.h
|
||||
!Edrivers/base/dma-coherent.c
|
||||
!Edrivers/base/dma-mapping.c
|
||||
|
|
|
@ -233,66 +233,78 @@ certainly invest a bit more effort into libata core layer).
|
|||
6. List of managed interfaces
|
||||
-----------------------------
|
||||
|
||||
MEM
|
||||
devm_kzalloc()
|
||||
devm_kfree()
|
||||
devm_kmemdup()
|
||||
devm_get_free_pages()
|
||||
devm_free_pages()
|
||||
CLOCK
|
||||
devm_clk_get()
|
||||
devm_clk_put()
|
||||
|
||||
DMA
|
||||
dmam_alloc_coherent()
|
||||
dmam_alloc_noncoherent()
|
||||
dmam_declare_coherent_memory()
|
||||
dmam_free_coherent()
|
||||
dmam_free_noncoherent()
|
||||
dmam_pool_create()
|
||||
dmam_pool_destroy()
|
||||
|
||||
GPIO
|
||||
devm_gpiod_get()
|
||||
devm_gpiod_get_index()
|
||||
devm_gpiod_get_index_optional()
|
||||
devm_gpiod_get_optional()
|
||||
devm_gpiod_put()
|
||||
|
||||
IIO
|
||||
devm_iio_device_alloc()
|
||||
devm_iio_device_free()
|
||||
devm_iio_trigger_alloc()
|
||||
devm_iio_trigger_free()
|
||||
devm_iio_device_register()
|
||||
devm_iio_device_unregister()
|
||||
devm_iio_trigger_alloc()
|
||||
devm_iio_trigger_free()
|
||||
|
||||
IO region
|
||||
devm_request_region()
|
||||
devm_request_mem_region()
|
||||
devm_release_region()
|
||||
devm_release_mem_region()
|
||||
|
||||
IRQ
|
||||
devm_request_irq()
|
||||
devm_free_irq()
|
||||
|
||||
DMA
|
||||
dmam_alloc_coherent()
|
||||
dmam_free_coherent()
|
||||
dmam_alloc_noncoherent()
|
||||
dmam_free_noncoherent()
|
||||
dmam_declare_coherent_memory()
|
||||
dmam_pool_create()
|
||||
dmam_pool_destroy()
|
||||
|
||||
PCI
|
||||
pcim_enable_device() : after success, all PCI ops become managed
|
||||
pcim_pin_device() : keep PCI device enabled after release
|
||||
devm_release_region()
|
||||
devm_request_mem_region()
|
||||
devm_request_region()
|
||||
|
||||
IOMAP
|
||||
devm_ioport_map()
|
||||
devm_ioport_unmap()
|
||||
devm_ioremap()
|
||||
devm_ioremap_nocache()
|
||||
devm_iounmap()
|
||||
devm_ioremap_resource() : checks resource, requests memory region, ioremaps
|
||||
devm_request_and_ioremap() : obsoleted by devm_ioremap_resource()
|
||||
devm_iounmap()
|
||||
pcim_iomap()
|
||||
pcim_iounmap()
|
||||
pcim_iomap_table() : array of mapped addresses indexed by BAR
|
||||
pcim_iomap_regions() : do request_region() and iomap() on multiple BARs
|
||||
pcim_iomap_table() : array of mapped addresses indexed by BAR
|
||||
pcim_iounmap()
|
||||
|
||||
REGULATOR
|
||||
devm_regulator_get()
|
||||
devm_regulator_put()
|
||||
devm_regulator_bulk_get()
|
||||
devm_regulator_register()
|
||||
IRQ
|
||||
devm_free_irq()
|
||||
devm_request_irq()
|
||||
|
||||
CLOCK
|
||||
devm_clk_get()
|
||||
devm_clk_put()
|
||||
MDIO
|
||||
devm_mdiobus_alloc()
|
||||
devm_mdiobus_alloc_size()
|
||||
devm_mdiobus_free()
|
||||
|
||||
MEM
|
||||
devm_free_pages()
|
||||
devm_get_free_pages()
|
||||
devm_kcalloc()
|
||||
devm_kfree()
|
||||
devm_kmalloc()
|
||||
devm_kmalloc_array()
|
||||
devm_kmemdup()
|
||||
devm_kzalloc()
|
||||
|
||||
PCI
|
||||
pcim_enable_device() : after success, all PCI ops become managed
|
||||
pcim_pin_device() : keep PCI device enabled after release
|
||||
|
||||
PHY
|
||||
devm_usb_get_phy()
|
||||
devm_usb_put_phy()
|
||||
|
||||
PINCTRL
|
||||
devm_pinctrl_get()
|
||||
|
@ -302,24 +314,14 @@ PWM
|
|||
devm_pwm_get()
|
||||
devm_pwm_put()
|
||||
|
||||
PHY
|
||||
devm_usb_get_phy()
|
||||
devm_usb_put_phy()
|
||||
REGULATOR
|
||||
devm_regulator_bulk_get()
|
||||
devm_regulator_get()
|
||||
devm_regulator_put()
|
||||
devm_regulator_register()
|
||||
|
||||
SLAVE DMA ENGINE
|
||||
devm_acpi_dma_controller_register()
|
||||
|
||||
SPI
|
||||
devm_spi_register_master()
|
||||
|
||||
GPIO
|
||||
devm_gpiod_get()
|
||||
devm_gpiod_get_index()
|
||||
devm_gpiod_get_optional()
|
||||
devm_gpiod_get_index_optional()
|
||||
devm_gpiod_put()
|
||||
|
||||
MDIO
|
||||
devm_mdiobus_alloc()
|
||||
devm_mdiobus_alloc_size()
|
||||
devm_mdiobus_free()
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
if(request_firmware(&fw_entry, $FIRMWARE, device) == 0)
|
||||
copy_fw_to_device(fw_entry->data, fw_entry->size);
|
||||
release(fw_entry);
|
||||
release_firmware(fw_entry);
|
||||
|
||||
Sample/simple hotplug script:
|
||||
============================
|
||||
|
@ -74,7 +74,7 @@
|
|||
HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
|
||||
|
||||
echo 1 > /sys/$DEVPATH/loading
|
||||
cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
|
||||
cat $HOTPLUG_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data
|
||||
echo 0 > /sys/$DEVPATH/loading
|
||||
|
||||
Random notes:
|
||||
|
@ -123,6 +123,6 @@
|
|||
--------------------
|
||||
After firmware cache mechanism is introduced during system sleep,
|
||||
request_firmware can be called safely inside device's suspend and
|
||||
resume callback, and callers need't cache the firmware by
|
||||
resume callback, and callers needn't cache the firmware by
|
||||
themselves any more for dealing with firmware loss during system
|
||||
resume.
|
||||
|
|
|
@ -29,6 +29,9 @@ Rules on what kind of patches are accepted, and which ones are not, into the
|
|||
|
||||
Procedure for submitting patches to the -stable tree:
|
||||
|
||||
- If the patch covers files in net/ or drivers/net please follow netdev stable
|
||||
submission guidelines as described in
|
||||
Documentation/networking/netdev-FAQ.txt
|
||||
- Send the patch, after verifying that it follows the above rules, to
|
||||
stable@vger.kernel.org. You must note the upstream commit ID in the
|
||||
changelog of your submission, as well as the kernel version you wish
|
||||
|
|
|
@ -2915,8 +2915,8 @@ S: Maintained
|
|||
L: linux-media@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: linaro-mm-sig@lists.linaro.org
|
||||
F: drivers/base/dma-buf*
|
||||
F: include/linux/dma-buf*
|
||||
F: drivers/dma-buf/
|
||||
F: include/linux/dma-buf* include/linux/reservation.h include/linux/*fence.h
|
||||
F: Documentation/dma-buf-sharing.txt
|
||||
T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git
|
||||
|
||||
|
|
|
@ -60,8 +60,7 @@ static void bcm21664_restart(enum reboot_mode mode, const char *cmd)
|
|||
|
||||
static void __init bcm21664_init(void)
|
||||
{
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL,
|
||||
&platform_bus);
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
kona_l2_cache_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,7 @@ static void bcm281xx_restart(enum reboot_mode mode, const char *cmd)
|
|||
|
||||
static void __init bcm281xx_init(void)
|
||||
{
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL,
|
||||
&platform_bus);
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
kona_l2_cache_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -158,16 +158,16 @@ static void __init edb7211_init_late(void)
|
|||
gpio_request_array(edb7211_gpios, ARRAY_SIZE(edb7211_gpios));
|
||||
|
||||
platform_device_register(&edb7211_flash_pdev);
|
||||
platform_device_register_data(&platform_bus, "platform-lcd", 0,
|
||||
platform_device_register_data(NULL, "platform-lcd", 0,
|
||||
&edb7211_lcd_power_pdata,
|
||||
sizeof(edb7211_lcd_power_pdata));
|
||||
platform_device_register_data(&platform_bus, "generic-bl", 0,
|
||||
platform_device_register_data(NULL, "generic-bl", 0,
|
||||
&edb7211_lcd_backlight_pdata,
|
||||
sizeof(edb7211_lcd_backlight_pdata));
|
||||
platform_device_register_simple("video-clps711x", 0, NULL, 0);
|
||||
platform_device_register_simple("cs89x0", 0, edb7211_cs8900_resource,
|
||||
ARRAY_SIZE(edb7211_cs8900_resource));
|
||||
platform_device_register_data(&platform_bus, "i2c-gpio", 0,
|
||||
platform_device_register_data(NULL, "i2c-gpio", 0,
|
||||
&edb7211_i2c_pdata,
|
||||
sizeof(edb7211_i2c_pdata));
|
||||
}
|
||||
|
|
|
@ -348,14 +348,14 @@ static void __init p720t_init_late(void)
|
|||
{
|
||||
WARN_ON(gpio_request_array(p720t_gpios, ARRAY_SIZE(p720t_gpios)));
|
||||
|
||||
platform_device_register_data(&platform_bus, "platform-lcd", 0,
|
||||
platform_device_register_data(NULL, "platform-lcd", 0,
|
||||
&p720t_lcd_power_pdata,
|
||||
sizeof(p720t_lcd_power_pdata));
|
||||
platform_device_register_data(&platform_bus, "generic-bl", 0,
|
||||
platform_device_register_data(NULL, "generic-bl", 0,
|
||||
&p720t_lcd_backlight_pdata,
|
||||
sizeof(p720t_lcd_backlight_pdata));
|
||||
platform_device_register_simple("video-clps711x", 0, NULL, 0);
|
||||
platform_device_register_data(&platform_bus, "leds-gpio", 0,
|
||||
platform_device_register_data(NULL, "leds-gpio", 0,
|
||||
&p720t_gpio_led_pdata,
|
||||
sizeof(p720t_gpio_led_pdata));
|
||||
}
|
||||
|
|
|
@ -24,12 +24,10 @@
|
|||
|
||||
struct device mxc_aips_bus = {
|
||||
.init_name = "mxc_aips",
|
||||
.parent = &platform_bus,
|
||||
};
|
||||
|
||||
struct device mxc_ahb_bus = {
|
||||
.init_name = "mxc_ahb",
|
||||
.parent = &platform_bus,
|
||||
};
|
||||
|
||||
int __init mxc_device_init(void)
|
||||
|
|
|
@ -245,7 +245,7 @@ static void __init mx27ads_regulator_init(void)
|
|||
vchip->set = vgpio_set;
|
||||
gpiochip_add(vchip);
|
||||
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage",
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage",
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&mx27ads_lcd_regulator_pdata,
|
||||
sizeof(mx27ads_lcd_regulator_pdata));
|
||||
|
|
|
@ -248,29 +248,29 @@ static void __init ape6evm_add_standard_devices(void)
|
|||
|
||||
regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "smsc911x", -1,
|
||||
platform_device_register_resndata(NULL, "smsc911x", -1,
|
||||
lan9220_res, ARRAY_SIZE(lan9220_res),
|
||||
&lan9220_data, sizeof(lan9220_data));
|
||||
|
||||
regulator_register_always_on(1, "MMC0 Vcc", vcc_mmc0_consumers,
|
||||
ARRAY_SIZE(vcc_mmc0_consumers), 2800000);
|
||||
platform_device_register_resndata(&platform_bus, "sh_mmcif", 0,
|
||||
platform_device_register_resndata(NULL, "sh_mmcif", 0,
|
||||
mmcif0_resources, ARRAY_SIZE(mmcif0_resources),
|
||||
&mmcif0_pdata, sizeof(mmcif0_pdata));
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", 2,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", 2,
|
||||
&vcc_sdhi0_info, sizeof(vcc_sdhi0_info));
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 0,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 0,
|
||||
sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
|
||||
&sdhi0_pdata, sizeof(sdhi0_pdata));
|
||||
regulator_register_always_on(3, "SDHI1 Vcc", vcc_sdhi1_consumers,
|
||||
ARRAY_SIZE(vcc_sdhi1_consumers), 3300000);
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 1,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 1,
|
||||
sdhi1_resources, ARRAY_SIZE(sdhi1_resources),
|
||||
&sdhi1_pdata, sizeof(sdhi1_pdata));
|
||||
platform_device_register_data(&platform_bus, "gpio-keys", -1,
|
||||
platform_device_register_data(NULL, "gpio-keys", -1,
|
||||
&ape6evm_keys_pdata,
|
||||
sizeof(ape6evm_keys_pdata));
|
||||
platform_device_register_data(&platform_bus, "leds-gpio", -1,
|
||||
platform_device_register_data(NULL, "leds-gpio", -1,
|
||||
&ape6evm_leds_pdata,
|
||||
sizeof(ape6evm_leds_pdata));
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ static struct renesas_usbhs_platform_info usbhs_info __initdata = {
|
|||
#define USB1_DEVICE "renesas_usbhs"
|
||||
#define ADD_USB_FUNC_DEVICE_IF_POSSIBLE() \
|
||||
platform_device_register_resndata( \
|
||||
&platform_bus, "renesas_usbhs", -1, \
|
||||
NULL, "renesas_usbhs", -1, \
|
||||
usbhsf_resources, \
|
||||
ARRAY_SIZE(usbhsf_resources), \
|
||||
&usbhs_info, sizeof(struct renesas_usbhs_platform_info))
|
||||
|
@ -236,7 +236,6 @@ static struct sh_eth_plat_data ether_platform_data __initdata = {
|
|||
};
|
||||
|
||||
static struct platform_device_info ether_info __initdata = {
|
||||
.parent = &platform_bus,
|
||||
.name = "r8a777x-ether",
|
||||
.id = -1,
|
||||
.res = ether_resources,
|
||||
|
@ -322,7 +321,6 @@ static struct resource vin##idx##_resources[] __initdata = { \
|
|||
}; \
|
||||
\
|
||||
static struct platform_device_info vin##idx##_info __initdata = { \
|
||||
.parent = &platform_bus, \
|
||||
.name = "r8a7778-vin", \
|
||||
.id = idx, \
|
||||
.res = vin##idx##_resources, \
|
||||
|
@ -621,10 +619,10 @@ static void __init bockw_init(void)
|
|||
/* VIN1 has a pin conflict with Ether */
|
||||
if (!IS_ENABLED(CONFIG_SH_ETH))
|
||||
platform_device_register_full(&vin1_info);
|
||||
platform_device_register_data(&platform_bus, "soc-camera-pdrv", 0,
|
||||
platform_device_register_data(NULL, "soc-camera-pdrv", 0,
|
||||
&iclink0_ml86v7667,
|
||||
sizeof(iclink0_ml86v7667));
|
||||
platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1,
|
||||
platform_device_register_data(NULL, "soc-camera-pdrv", 1,
|
||||
&iclink1_ml86v7667,
|
||||
sizeof(iclink1_ml86v7667));
|
||||
|
||||
|
@ -637,12 +635,12 @@ static void __init bockw_init(void)
|
|||
r8a7778_pinmux_init();
|
||||
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "sh_mmcif", -1,
|
||||
NULL, "sh_mmcif", -1,
|
||||
mmc_resources, ARRAY_SIZE(mmc_resources),
|
||||
&sh_mmcif_plat, sizeof(struct sh_mmcif_plat_data));
|
||||
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "rcar_usb_phy", -1,
|
||||
NULL, "rcar_usb_phy", -1,
|
||||
usb_phy_resources,
|
||||
ARRAY_SIZE(usb_phy_resources),
|
||||
&usb_phy_platform_data,
|
||||
|
@ -668,7 +666,7 @@ static void __init bockw_init(void)
|
|||
iowrite16(val, fpga + IRQ0MR);
|
||||
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "smsc911x", -1,
|
||||
NULL, "smsc911x", -1,
|
||||
smsc911x_resources, ARRAY_SIZE(smsc911x_resources),
|
||||
&smsc911x_data, sizeof(smsc911x_data));
|
||||
}
|
||||
|
@ -685,7 +683,7 @@ static void __init bockw_init(void)
|
|||
iounmap(base);
|
||||
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "sh_mobile_sdhi", 0,
|
||||
NULL, "sh_mobile_sdhi", 0,
|
||||
sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
|
||||
&sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
}
|
||||
|
@ -700,7 +698,7 @@ static void __init bockw_init(void)
|
|||
"ak4554-adc-dac", 1, NULL, 0);
|
||||
|
||||
pdev = platform_device_register_resndata(
|
||||
&platform_bus, "rcar_sound", -1,
|
||||
NULL, "rcar_sound", -1,
|
||||
rsnd_resources, ARRAY_SIZE(rsnd_resources),
|
||||
&rsnd_info, sizeof(rsnd_info));
|
||||
|
||||
|
@ -710,7 +708,6 @@ static void __init bockw_init(void)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(rsnd_card_info); i++) {
|
||||
struct platform_device_info cardinfo = {
|
||||
.parent = &platform_bus,
|
||||
.name = "asoc-simple-card",
|
||||
.id = i,
|
||||
.data = &rsnd_card_info[i],
|
||||
|
|
|
@ -46,7 +46,6 @@ static const struct resource ether_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info ether_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "r7s72100-ether",
|
||||
.id = -1,
|
||||
.res = ether_resources,
|
||||
|
@ -76,7 +75,7 @@ static const struct rspi_plat_data rspi_pdata __initconst = {
|
|||
};
|
||||
|
||||
#define r7s72100_register_rspi(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "rspi-rz", idx, \
|
||||
platform_device_register_resndata(NULL, "rspi-rz", idx, \
|
||||
rspi##idx##_resources, \
|
||||
ARRAY_SIZE(rspi##idx##_resources), \
|
||||
&rspi_pdata, sizeof(rspi_pdata))
|
||||
|
@ -118,7 +117,7 @@ R7S72100_SCIF(6, 0xe800a000, gic_iid(245));
|
|||
R7S72100_SCIF(7, 0xe800a800, gic_iid(249));
|
||||
|
||||
#define r7s72100_register_scif(index) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-sci", index, \
|
||||
platform_device_register_resndata(NULL, "sh-sci", index, \
|
||||
scif##index##_resources, \
|
||||
ARRAY_SIZE(scif##index##_resources), \
|
||||
&scif##index##_platform_data, \
|
||||
|
|
|
@ -118,7 +118,6 @@ static const struct resource ether_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info ether_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "r8a7791-ether",
|
||||
.id = -1,
|
||||
.res = ether_resources,
|
||||
|
@ -230,7 +229,6 @@ static const struct resource sata0_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info sata0_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "sata-r8a7791",
|
||||
.id = 0,
|
||||
.res = sata0_resources,
|
||||
|
@ -439,13 +437,13 @@ static void __init koelsch_add_standard_devices(void)
|
|||
r8a7791_pinmux_init();
|
||||
r8a7791_add_standard_devices();
|
||||
platform_device_register_full(ðer_info);
|
||||
platform_device_register_data(&platform_bus, "leds-gpio", -1,
|
||||
platform_device_register_data(NULL, "leds-gpio", -1,
|
||||
&koelsch_leds_pdata,
|
||||
sizeof(koelsch_leds_pdata));
|
||||
platform_device_register_data(&platform_bus, "gpio-keys", -1,
|
||||
platform_device_register_data(NULL, "gpio-keys", -1,
|
||||
&koelsch_keys_pdata,
|
||||
sizeof(koelsch_keys_pdata));
|
||||
platform_device_register_resndata(&platform_bus, "qspi", 0,
|
||||
platform_device_register_resndata(NULL, "qspi", 0,
|
||||
qspi_resources,
|
||||
ARRAY_SIZE(qspi_resources),
|
||||
&qspi_pdata, sizeof(qspi_pdata));
|
||||
|
@ -460,28 +458,28 @@ static void __init koelsch_add_standard_devices(void)
|
|||
koelsch_add_i2c(4);
|
||||
koelsch_add_i2c(5);
|
||||
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", 0,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", 0,
|
||||
&vcc_sdhi0_info, sizeof(struct fixed_voltage_config));
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", 1,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", 1,
|
||||
&vcc_sdhi1_info, sizeof(struct fixed_voltage_config));
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", 2,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", 2,
|
||||
&vcc_sdhi2_info, sizeof(struct fixed_voltage_config));
|
||||
platform_device_register_data(&platform_bus, "gpio-regulator", 0,
|
||||
platform_device_register_data(NULL, "gpio-regulator", 0,
|
||||
&vccq_sdhi0_info, sizeof(struct gpio_regulator_config));
|
||||
platform_device_register_data(&platform_bus, "gpio-regulator", 1,
|
||||
platform_device_register_data(NULL, "gpio-regulator", 1,
|
||||
&vccq_sdhi1_info, sizeof(struct gpio_regulator_config));
|
||||
platform_device_register_data(&platform_bus, "gpio-regulator", 2,
|
||||
platform_device_register_data(NULL, "gpio-regulator", 2,
|
||||
&vccq_sdhi2_info, sizeof(struct gpio_regulator_config));
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 0,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 0,
|
||||
sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
|
||||
&sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 1,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 1,
|
||||
sdhi1_resources, ARRAY_SIZE(sdhi1_resources),
|
||||
&sdhi1_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 2,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 2,
|
||||
sdhi2_resources, ARRAY_SIZE(sdhi2_resources),
|
||||
&sdhi2_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
|
||||
|
|
|
@ -277,7 +277,6 @@ static const struct resource ether_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info ether_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "r8a7790-ether",
|
||||
.id = -1,
|
||||
.res = ether_resources,
|
||||
|
@ -354,7 +353,6 @@ static void __init lager_add_vin_device(unsigned idx,
|
|||
struct rcar_vin_platform_data *pdata)
|
||||
{
|
||||
struct platform_device_info vin_info = {
|
||||
.parent = &platform_bus,
|
||||
.name = "r8a7790-vin",
|
||||
.id = idx,
|
||||
.res = &vin_resources[idx * 2],
|
||||
|
@ -391,7 +389,7 @@ LAGER_CAMERA(1, "adv7180", 0x20, NULL, RCAR_VIN_BT656);
|
|||
|
||||
static void __init lager_add_camera1_device(void)
|
||||
{
|
||||
platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1,
|
||||
platform_device_register_data(NULL, "soc-camera-pdrv", 1,
|
||||
&cam1_link, sizeof(cam1_link));
|
||||
lager_add_vin_device(1, &vin1_pdata);
|
||||
}
|
||||
|
@ -403,7 +401,6 @@ static const struct resource sata1_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info sata1_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "sata-r8a7790",
|
||||
.id = 1,
|
||||
.res = sata1_resources,
|
||||
|
@ -533,7 +530,7 @@ static struct usbhs_private usbhs_priv __initdata = {
|
|||
static void __init lager_register_usbhs(void)
|
||||
{
|
||||
usb_bind_phy("renesas_usbhs", 0, "usb_phy_rcar_gen2");
|
||||
platform_device_register_resndata(&platform_bus,
|
||||
platform_device_register_resndata(NULL,
|
||||
"renesas_usbhs", -1,
|
||||
usbhs_resources,
|
||||
ARRAY_SIZE(usbhs_resources),
|
||||
|
@ -608,7 +605,6 @@ static struct asoc_simple_card_info rsnd_card_info = {
|
|||
static void __init lager_add_rsnd_device(void)
|
||||
{
|
||||
struct platform_device_info cardinfo = {
|
||||
.parent = &platform_bus,
|
||||
.name = "asoc-simple-card",
|
||||
.id = -1,
|
||||
.data = &rsnd_card_info,
|
||||
|
@ -620,7 +616,7 @@ static void __init lager_add_rsnd_device(void)
|
|||
ARRAY_SIZE(i2c2_devices));
|
||||
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "rcar_sound", -1,
|
||||
NULL, "rcar_sound", -1,
|
||||
rsnd_resources, ARRAY_SIZE(rsnd_resources),
|
||||
&rsnd_info, sizeof(rsnd_info));
|
||||
|
||||
|
@ -663,7 +659,6 @@ static const struct resource pci1_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info pci1_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "pci-rcar-gen2",
|
||||
.id = 1,
|
||||
.res = pci1_resources,
|
||||
|
@ -684,7 +679,6 @@ static const struct resource pci2_resources[] __initconst = {
|
|||
};
|
||||
|
||||
static const struct platform_device_info pci2_info __initconst = {
|
||||
.parent = &platform_bus,
|
||||
.name = "pci-rcar-gen2",
|
||||
.id = 2,
|
||||
.res = pci2_resources,
|
||||
|
@ -795,16 +789,16 @@ static void __init lager_add_standard_devices(void)
|
|||
r8a7790_pinmux_init();
|
||||
|
||||
r8a7790_add_standard_devices();
|
||||
platform_device_register_data(&platform_bus, "leds-gpio", -1,
|
||||
platform_device_register_data(NULL, "leds-gpio", -1,
|
||||
&lager_leds_pdata,
|
||||
sizeof(lager_leds_pdata));
|
||||
platform_device_register_data(&platform_bus, "gpio-keys", -1,
|
||||
platform_device_register_data(NULL, "gpio-keys", -1,
|
||||
&lager_keys_pdata,
|
||||
sizeof(lager_keys_pdata));
|
||||
regulator_register_always_on(fixed_regulator_idx++,
|
||||
"fixed-3.3V", fixed3v3_power_consumers,
|
||||
ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
|
||||
platform_device_register_resndata(&platform_bus, "sh_mmcif", 1,
|
||||
platform_device_register_resndata(NULL, "sh_mmcif", 1,
|
||||
mmcif1_resources, ARRAY_SIZE(mmcif1_resources),
|
||||
&mmcif1_pdata, sizeof(mmcif1_pdata));
|
||||
|
||||
|
@ -812,27 +806,27 @@ static void __init lager_add_standard_devices(void)
|
|||
|
||||
lager_add_du_device();
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "qspi", 0,
|
||||
platform_device_register_resndata(NULL, "qspi", 0,
|
||||
qspi_resources,
|
||||
ARRAY_SIZE(qspi_resources),
|
||||
&qspi_pdata, sizeof(qspi_pdata));
|
||||
spi_register_board_info(spi_info, ARRAY_SIZE(spi_info));
|
||||
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", fixed_regulator_idx++,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", fixed_regulator_idx++,
|
||||
&vcc_sdhi0_info, sizeof(struct fixed_voltage_config));
|
||||
platform_device_register_data(&platform_bus, "reg-fixed-voltage", fixed_regulator_idx++,
|
||||
platform_device_register_data(NULL, "reg-fixed-voltage", fixed_regulator_idx++,
|
||||
&vcc_sdhi2_info, sizeof(struct fixed_voltage_config));
|
||||
|
||||
platform_device_register_data(&platform_bus, "gpio-regulator", gpio_regulator_idx++,
|
||||
platform_device_register_data(NULL, "gpio-regulator", gpio_regulator_idx++,
|
||||
&vccq_sdhi0_info, sizeof(struct gpio_regulator_config));
|
||||
platform_device_register_data(&platform_bus, "gpio-regulator", gpio_regulator_idx++,
|
||||
platform_device_register_data(NULL, "gpio-regulator", gpio_regulator_idx++,
|
||||
&vccq_sdhi2_info, sizeof(struct gpio_regulator_config));
|
||||
|
||||
lager_add_camera1_device();
|
||||
|
||||
platform_device_register_full(&sata1_info);
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "usb_phy_rcar_gen2",
|
||||
platform_device_register_resndata(NULL, "usb_phy_rcar_gen2",
|
||||
-1, usbhs_phy_resources,
|
||||
ARRAY_SIZE(usbhs_phy_resources),
|
||||
&usbhs_phy_pdata,
|
||||
|
@ -843,10 +837,10 @@ static void __init lager_add_standard_devices(void)
|
|||
|
||||
lager_add_rsnd_device();
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 0,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 0,
|
||||
sdhi0_resources, ARRAY_SIZE(sdhi0_resources),
|
||||
&sdhi0_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
platform_device_register_resndata(&platform_bus, "sh_mobile_sdhi", 2,
|
||||
platform_device_register_resndata(NULL, "sh_mobile_sdhi", 2,
|
||||
sdhi2_resources, ARRAY_SIZE(sdhi2_resources),
|
||||
&sdhi2_info, sizeof(struct sh_mobile_sdhi_info));
|
||||
}
|
||||
|
|
|
@ -272,7 +272,6 @@ static struct resource vin##idx##_resources[] __initdata = { \
|
|||
}; \
|
||||
\
|
||||
static struct platform_device_info vin##idx##_info __initdata = { \
|
||||
.parent = &platform_bus, \
|
||||
.name = "r8a7779-vin", \
|
||||
.id = idx, \
|
||||
.res = vin##idx##_resources, \
|
||||
|
|
|
@ -33,7 +33,7 @@ static struct resource mtu2_resources[] __initdata = {
|
|||
};
|
||||
|
||||
#define r7s72100_register_mtu2() \
|
||||
platform_device_register_resndata(&platform_bus, "sh-mtu2", \
|
||||
platform_device_register_resndata(NULL, "sh-mtu2", \
|
||||
-1, mtu2_resources, \
|
||||
ARRAY_SIZE(mtu2_resources), \
|
||||
NULL, 0)
|
||||
|
|
|
@ -68,7 +68,7 @@ R8A73A4_SCIFB(4, 0xe6ce0000, gic_spi(150)); /* SCIFB2 */
|
|||
R8A73A4_SCIFB(5, 0xe6cf0000, gic_spi(151)); /* SCIFB3 */
|
||||
|
||||
#define r8a73a4_register_scif(index) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-sci", index, \
|
||||
platform_device_register_resndata(NULL, "sh-sci", index, \
|
||||
scif##index##_resources, \
|
||||
ARRAY_SIZE(scif##index##_resources), \
|
||||
&scif##index##_platform_data, \
|
||||
|
@ -149,7 +149,7 @@ static const struct resource irqc1_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a73a4_register_irqc(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "renesas_irqc", \
|
||||
platform_device_register_resndata(NULL, "renesas_irqc", \
|
||||
idx, irqc##idx##_resources, \
|
||||
ARRAY_SIZE(irqc##idx##_resources), \
|
||||
&irqc##idx##_data, \
|
||||
|
@ -179,7 +179,7 @@ static struct resource cmt1_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a7790_register_cmt(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
|
||||
platform_device_register_resndata(NULL, "sh-cmt-48-gen2", \
|
||||
idx, cmt##idx##_resources, \
|
||||
ARRAY_SIZE(cmt##idx##_resources), \
|
||||
&cmt##idx##_platform_data, \
|
||||
|
@ -280,7 +280,7 @@ static struct resource dma_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a73a4_register_dmac() \
|
||||
platform_device_register_resndata(&platform_bus, "sh-dma-engine", 0, \
|
||||
platform_device_register_resndata(NULL, "sh-dma-engine", 0, \
|
||||
dma_resources, ARRAY_SIZE(dma_resources), \
|
||||
&dma_pdata, sizeof(dma_pdata))
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ R8A7778_SCIF(4, 0xffe44000, gic_iid(0x6a));
|
|||
R8A7778_SCIF(5, 0xffe45000, gic_iid(0x6b));
|
||||
|
||||
#define r8a7778_register_scif(index) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-sci", index, \
|
||||
platform_device_register_resndata(NULL, "sh-sci", index, \
|
||||
scif##index##_resources, \
|
||||
ARRAY_SIZE(scif##index##_resources), \
|
||||
&scif##index##_platform_data, \
|
||||
|
@ -84,7 +84,7 @@ static struct resource sh_tmu0_resources[] = {
|
|||
|
||||
#define r8a7778_register_tmu(idx) \
|
||||
platform_device_register_resndata( \
|
||||
&platform_bus, "sh-tmu", idx, \
|
||||
NULL, "sh-tmu", idx, \
|
||||
sh_tmu##idx##_resources, \
|
||||
ARRAY_SIZE(sh_tmu##idx##_resources), \
|
||||
&sh_tmu##idx##_platform_data, \
|
||||
|
@ -173,7 +173,6 @@ static struct resource ohci_resources[] __initdata = {
|
|||
|
||||
#define USB_PLATFORM_INFO(hci) \
|
||||
static struct platform_device_info hci##_info __initdata = { \
|
||||
.parent = &platform_bus, \
|
||||
.name = #hci "-platform", \
|
||||
.id = -1, \
|
||||
.res = hci##_resources, \
|
||||
|
@ -212,7 +211,7 @@ R8A7778_GPIO(4);
|
|||
|
||||
#define r8a7778_register_gpio(idx) \
|
||||
platform_device_register_resndata( \
|
||||
&platform_bus, "gpio_rcar", idx, \
|
||||
NULL, "gpio_rcar", idx, \
|
||||
r8a7778_gpio##idx##_resources, \
|
||||
ARRAY_SIZE(r8a7778_gpio##idx##_resources), \
|
||||
&r8a7778_gpio##idx##_platform_data, \
|
||||
|
@ -496,8 +495,8 @@ static struct resource hpb_dmae_resources[] __initdata = {
|
|||
|
||||
static void __init r8a7778_register_hpb_dmae(void)
|
||||
{
|
||||
platform_device_register_resndata(&platform_bus, "hpb-dma-engine", -1,
|
||||
hpb_dmae_resources,
|
||||
platform_device_register_resndata(NULL, "hpb-dma-engine",
|
||||
-1, hpb_dmae_resources,
|
||||
ARRAY_SIZE(hpb_dmae_resources),
|
||||
&dma_platform_data,
|
||||
sizeof(dma_platform_data));
|
||||
|
@ -565,7 +564,7 @@ void __init r8a7778_init_irq_extpin(int irlm)
|
|||
r8a7778_init_irq_extpin_dt(irlm);
|
||||
if (irlm)
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "renesas_intc_irqpin", -1,
|
||||
NULL, "renesas_intc_irqpin", -1,
|
||||
irqpin_resources, ARRAY_SIZE(irqpin_resources),
|
||||
&irqpin_platform_data, sizeof(irqpin_platform_data));
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ void __init r8a7779_init_irq_extpin(int irlm)
|
|||
r8a7779_init_irq_extpin_dt(irlm);
|
||||
if (irlm)
|
||||
platform_device_register_resndata(
|
||||
&platform_bus, "renesas_intc_irqpin", -1,
|
||||
NULL, "renesas_intc_irqpin", -1,
|
||||
irqpin0_resources, ARRAY_SIZE(irqpin0_resources),
|
||||
&irqpin0_platform_data, sizeof(irqpin0_platform_data));
|
||||
}
|
||||
|
@ -632,8 +632,8 @@ static struct resource hpb_dmae_resources[] __initdata = {
|
|||
|
||||
static void __init r8a7779_register_hpb_dmae(void)
|
||||
{
|
||||
platform_device_register_resndata(&platform_bus, "hpb-dma-engine", -1,
|
||||
hpb_dmae_resources,
|
||||
platform_device_register_resndata(NULL, "hpb-dma-engine",
|
||||
-1, hpb_dmae_resources,
|
||||
ARRAY_SIZE(hpb_dmae_resources),
|
||||
&dma_platform_data,
|
||||
sizeof(dma_platform_data));
|
||||
|
|
|
@ -113,7 +113,7 @@ static struct resource r8a7790_audio_dmac_resources[] = {
|
|||
|
||||
#define r8a7790_register_audio_dmac(id) \
|
||||
platform_device_register_resndata( \
|
||||
&platform_bus, "sh-dma-engine", id, \
|
||||
NULL, "sh-dma-engine", id, \
|
||||
&r8a7790_audio_dmac_resources[id * 3], 3, \
|
||||
&r8a7790_audio_dmac_platform_data, \
|
||||
sizeof(r8a7790_audio_dmac_platform_data))
|
||||
|
@ -149,7 +149,7 @@ R8A7790_GPIO(4);
|
|||
R8A7790_GPIO(5);
|
||||
|
||||
#define r8a7790_register_gpio(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "gpio_rcar", idx, \
|
||||
platform_device_register_resndata(NULL, "gpio_rcar", idx, \
|
||||
r8a7790_gpio##idx##_resources, \
|
||||
ARRAY_SIZE(r8a7790_gpio##idx##_resources), \
|
||||
&r8a7790_gpio##idx##_platform_data, \
|
||||
|
@ -227,7 +227,7 @@ R8A7790_HSCIF(8, 0xe62c0000, gic_spi(154)); /* HSCIF0 */
|
|||
R8A7790_HSCIF(9, 0xe62c8000, gic_spi(155)); /* HSCIF1 */
|
||||
|
||||
#define r8a7790_register_scif(index) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-sci", index, \
|
||||
platform_device_register_resndata(NULL, "sh-sci", index, \
|
||||
scif##index##_resources, \
|
||||
ARRAY_SIZE(scif##index##_resources), \
|
||||
&scif##index##_platform_data, \
|
||||
|
@ -246,7 +246,7 @@ static const struct resource irqc0_resources[] __initconst = {
|
|||
};
|
||||
|
||||
#define r8a7790_register_irqc(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "renesas_irqc", \
|
||||
platform_device_register_resndata(NULL, "renesas_irqc", \
|
||||
idx, irqc##idx##_resources, \
|
||||
ARRAY_SIZE(irqc##idx##_resources), \
|
||||
&irqc##idx##_data, \
|
||||
|
@ -273,7 +273,7 @@ static struct resource cmt0_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a7790_register_cmt(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
|
||||
platform_device_register_resndata(NULL, "sh-cmt-48-gen2", \
|
||||
idx, cmt##idx##_resources, \
|
||||
ARRAY_SIZE(cmt##idx##_resources), \
|
||||
&cmt##idx##_platform_data, \
|
||||
|
|
|
@ -65,7 +65,7 @@ R8A7791_GPIO(6, 0xe6055400, 32);
|
|||
R8A7791_GPIO(7, 0xe6055800, 26);
|
||||
|
||||
#define r8a7791_register_gpio(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "gpio_rcar", idx, \
|
||||
platform_device_register_resndata(NULL, "gpio_rcar", idx, \
|
||||
r8a7791_gpio##idx##_resources, \
|
||||
ARRAY_SIZE(r8a7791_gpio##idx##_resources), \
|
||||
&r8a7791_gpio##idx##_platform_data, \
|
||||
|
@ -122,7 +122,7 @@ R8A7791_SCIFA(13, 0xe6c78000, gic_spi(30)); /* SCIFA4 */
|
|||
R8A7791_SCIFA(14, 0xe6c80000, gic_spi(31)); /* SCIFA5 */
|
||||
|
||||
#define r8a7791_register_scif(index) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-sci", index, \
|
||||
platform_device_register_resndata(NULL, "sh-sci", index, \
|
||||
scif##index##_resources, \
|
||||
ARRAY_SIZE(scif##index##_resources), \
|
||||
&scif##index##_platform_data, \
|
||||
|
@ -138,7 +138,7 @@ static struct resource cmt0_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a7791_register_cmt(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
|
||||
platform_device_register_resndata(NULL, "sh-cmt-48-gen2", \
|
||||
idx, cmt##idx##_resources, \
|
||||
ARRAY_SIZE(cmt##idx##_resources), \
|
||||
&cmt##idx##_platform_data, \
|
||||
|
@ -163,7 +163,7 @@ static struct resource irqc0_resources[] = {
|
|||
};
|
||||
|
||||
#define r8a7791_register_irqc(idx) \
|
||||
platform_device_register_resndata(&platform_bus, "renesas_irqc", \
|
||||
platform_device_register_resndata(NULL, "renesas_irqc", \
|
||||
idx, irqc##idx##_resources, \
|
||||
ARRAY_SIZE(irqc##idx##_resources), \
|
||||
&irqc##idx##_data, \
|
||||
|
|
|
@ -272,7 +272,7 @@ void __init puv3_core_init(void)
|
|||
platform_device_register_simple("PKUnity-v3-UART", 1,
|
||||
puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources));
|
||||
platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0);
|
||||
platform_device_register_resndata(&platform_bus, "musb_hdrc", -1,
|
||||
platform_device_register_resndata(NULL, "musb_hdrc", -1,
|
||||
puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources),
|
||||
&puv3_usb_plat, sizeof(puv3_usb_plat));
|
||||
}
|
||||
|
|
|
@ -112,13 +112,13 @@ int __init mach_nb0916_init(void)
|
|||
platform_device_register_simple("PKUnity-v3-I2C", -1,
|
||||
puv3_i2c_resources, ARRAY_SIZE(puv3_i2c_resources));
|
||||
|
||||
platform_device_register_data(&platform_bus, "pwm-backlight", -1,
|
||||
platform_device_register_data(NULL, "pwm-backlight", -1,
|
||||
&nb0916_backlight_data, sizeof(nb0916_backlight_data));
|
||||
|
||||
platform_device_register_data(&platform_bus, "gpio-keys", -1,
|
||||
platform_device_register_data(NULL, "gpio-keys", -1,
|
||||
&nb0916_gpio_button_data, sizeof(nb0916_gpio_button_data));
|
||||
|
||||
platform_device_register_resndata(&platform_bus, "physmap-flash", -1,
|
||||
platform_device_register_resndata(NULL, "physmap-flash", -1,
|
||||
&physmap_flash_resource, 1,
|
||||
&physmap_flash_data, sizeof(physmap_flash_data));
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ obj-$(CONFIG_FB_INTEL) += video/fbdev/intelfb/
|
|||
|
||||
obj-$(CONFIG_PARPORT) += parport/
|
||||
obj-y += base/ block/ misc/ mfd/ nfc/
|
||||
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
|
||||
obj-$(CONFIG_NUBUS) += nubus/
|
||||
obj-y += macintosh/
|
||||
obj-$(CONFIG_IDE) += ide/
|
||||
|
|
|
@ -149,15 +149,21 @@ config EXTRA_FIRMWARE_DIR
|
|||
some other directory containing the firmware files.
|
||||
|
||||
config FW_LOADER_USER_HELPER
|
||||
bool
|
||||
|
||||
config FW_LOADER_USER_HELPER_FALLBACK
|
||||
bool "Fallback user-helper invocation for firmware loading"
|
||||
depends on FW_LOADER
|
||||
default y
|
||||
select FW_LOADER_USER_HELPER
|
||||
help
|
||||
This option enables / disables the invocation of user-helper
|
||||
(e.g. udev) for loading firmware files as a fallback after the
|
||||
direct file loading in kernel fails. The user-mode helper is
|
||||
no longer required unless you have a special firmware file that
|
||||
resides in a non-standard path.
|
||||
resides in a non-standard path. Moreover, the udev support has
|
||||
been deprecated upstream.
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config DEBUG_DRIVER
|
||||
bool "Driver Core verbose debug messages"
|
||||
|
@ -208,6 +214,15 @@ config DMA_SHARED_BUFFER
|
|||
APIs extension; the file's descriptor can then be passed on to other
|
||||
driver.
|
||||
|
||||
config FENCE_TRACE
|
||||
bool "Enable verbose FENCE_TRACE messages"
|
||||
depends on DMA_SHARED_BUFFER
|
||||
help
|
||||
Enable the FENCE_TRACE printks. This will add extra
|
||||
spam to the console log, but will make it easier to diagnose
|
||||
lockup related problems for dma-buffers shared across multiple
|
||||
devices.
|
||||
|
||||
config DMA_CMA
|
||||
bool "DMA Contiguous Memory Allocator"
|
||||
depends on HAVE_DMA_CONTIGUOUS && CMA
|
||||
|
|
|
@ -10,7 +10,6 @@ obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
|
|||
obj-y += power/
|
||||
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
|
||||
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
|
||||
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o reservation.o
|
||||
obj-$(CONFIG_ISA) += isa.o
|
||||
obj-$(CONFIG_FW_LOADER) += firmware_class.o
|
||||
obj-$(CONFIG_NUMA) += node.o
|
||||
|
|
|
@ -18,6 +18,15 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct component_match {
|
||||
size_t alloc;
|
||||
size_t num;
|
||||
struct {
|
||||
void *data;
|
||||
int (*fn)(struct device *, void *);
|
||||
} compare[0];
|
||||
};
|
||||
|
||||
struct master {
|
||||
struct list_head node;
|
||||
struct list_head components;
|
||||
|
@ -25,6 +34,7 @@ struct master {
|
|||
|
||||
const struct component_master_ops *ops;
|
||||
struct device *dev;
|
||||
struct component_match *match;
|
||||
};
|
||||
|
||||
struct component {
|
||||
|
@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
|
|||
c->master = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to a master, finding the component via the compare
|
||||
* function and compare data. This is safe to call for duplicate matches
|
||||
* and will not result in the same component being added multiple times.
|
||||
*/
|
||||
int component_master_add_child(struct master *master,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
|
@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
|
|||
int ret = -ENXIO;
|
||||
|
||||
list_for_each_entry(c, &component_list, node) {
|
||||
if (c->master)
|
||||
if (c->master && c->master != master)
|
||||
continue;
|
||||
|
||||
if (compare(c->dev, compare_data)) {
|
||||
component_attach_master(master, c);
|
||||
if (!c->master)
|
||||
component_attach_master(master, c);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_child);
|
||||
|
||||
static int find_components(struct master *master)
|
||||
{
|
||||
struct component_match *match = master->match;
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!match) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
return master->ops->add_components(master->dev, master);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the array of match functions and attach
|
||||
* any components which are found to this master.
|
||||
*/
|
||||
for (i = 0; i < match->num; i++) {
|
||||
ret = component_master_add_child(master,
|
||||
match->compare[i].fn,
|
||||
match->compare[i].data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Detach all attached components from this master */
|
||||
static void master_remove_components(struct master *master)
|
||||
{
|
||||
|
@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
|
|||
static int try_to_bring_up_master(struct master *master,
|
||||
struct component *component)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!master->bound) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (master->ops->add_components(master->dev, master)) {
|
||||
/* Failed to find all components */
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (master->bound)
|
||||
return 0;
|
||||
|
||||
if (component && component->master != master) {
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
master_remove_components(master);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
ret = 1;
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (find_components(master)) {
|
||||
/* Failed to find all components */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (component && component->master != master) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
return 1;
|
||||
|
||||
out:
|
||||
master_remove_components(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
|
|||
master_remove_components(master);
|
||||
}
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
static size_t component_match_size(size_t num)
|
||||
{
|
||||
return offsetof(struct component_match, compare[num]);
|
||||
}
|
||||
|
||||
static struct component_match *component_match_realloc(struct device *dev,
|
||||
struct component_match *match, size_t num)
|
||||
{
|
||||
struct component_match *new;
|
||||
|
||||
if (match && match->alloc == num)
|
||||
return match;
|
||||
|
||||
new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
|
||||
if (!new)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (match) {
|
||||
memcpy(new, match, component_match_size(min(match->num, num)));
|
||||
devm_kfree(dev, match);
|
||||
} else {
|
||||
new->num = 0;
|
||||
}
|
||||
|
||||
new->alloc = num;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to be matched.
|
||||
*
|
||||
* The match array is first created or extended if necessary.
|
||||
*/
|
||||
void component_match_add(struct device *dev, struct component_match **matchptr,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
struct component_match *match = *matchptr;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
|
||||
if (!match || match->num == match->alloc) {
|
||||
size_t new_size = match ? match->alloc + 16 : 15;
|
||||
|
||||
match = component_match_realloc(dev, match, new_size);
|
||||
|
||||
*matchptr = match;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
}
|
||||
|
||||
match->compare[match->num].fn = compare;
|
||||
match->compare[match->num].data = compare_data;
|
||||
match->num++;
|
||||
}
|
||||
EXPORT_SYMBOL(component_match_add);
|
||||
|
||||
int component_master_add_with_match(struct device *dev,
|
||||
const struct component_master_ops *ops,
|
||||
struct component_match *match)
|
||||
{
|
||||
struct master *master;
|
||||
int ret;
|
||||
|
||||
if (ops->add_components && match)
|
||||
return -EINVAL;
|
||||
|
||||
if (match) {
|
||||
/* Reallocate the match array for its true size */
|
||||
match = component_match_realloc(dev, match, match->num);
|
||||
if (IS_ERR(match))
|
||||
return PTR_ERR(match);
|
||||
}
|
||||
|
||||
master = kzalloc(sizeof(*master), GFP_KERNEL);
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->dev = dev;
|
||||
master->ops = ops;
|
||||
master->match = match;
|
||||
INIT_LIST_HEAD(&master->components);
|
||||
|
||||
/* Add to the list of available masters. */
|
||||
|
@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
|
|||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_with_match);
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
{
|
||||
return component_master_add_with_match(dev, ops, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add);
|
||||
|
||||
void component_master_del(struct device *dev,
|
||||
|
|
|
@ -100,10 +100,16 @@ static inline long firmware_loading_timeout(void)
|
|||
#define FW_OPT_UEVENT (1U << 0)
|
||||
#define FW_OPT_NOWAIT (1U << 1)
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
#define FW_OPT_FALLBACK (1U << 2)
|
||||
#define FW_OPT_USERHELPER (1U << 2)
|
||||
#else
|
||||
#define FW_OPT_FALLBACK 0
|
||||
#define FW_OPT_USERHELPER 0
|
||||
#endif
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
|
||||
#define FW_OPT_FALLBACK FW_OPT_USERHELPER
|
||||
#else
|
||||
#define FW_OPT_FALLBACK 0
|
||||
#endif
|
||||
#define FW_OPT_NO_WARN (1U << 3)
|
||||
|
||||
struct firmware_cache {
|
||||
/* firmware_buf instance will be added into the below list */
|
||||
|
@ -279,26 +285,15 @@ static const char * const fw_path[] = {
|
|||
module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
|
||||
MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
|
||||
|
||||
/* Don't inline this: 'struct kstat' is biggish */
|
||||
static noinline_for_stack int fw_file_size(struct file *file)
|
||||
{
|
||||
struct kstat st;
|
||||
if (vfs_getattr(&file->f_path, &st))
|
||||
return -1;
|
||||
if (!S_ISREG(st.mode))
|
||||
return -1;
|
||||
if (st.size != (int)st.size)
|
||||
return -1;
|
||||
return st.size;
|
||||
}
|
||||
|
||||
static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
|
||||
{
|
||||
int size;
|
||||
char *buf;
|
||||
int rc;
|
||||
|
||||
size = fw_file_size(file);
|
||||
if (!S_ISREG(file_inode(file)->i_mode))
|
||||
return -EINVAL;
|
||||
size = i_size_read(file_inode(file));
|
||||
if (size <= 0)
|
||||
return -EINVAL;
|
||||
buf = vmalloc(size);
|
||||
|
@ -718,7 +713,7 @@ out:
|
|||
static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
|
||||
{
|
||||
struct firmware_buf *buf = fw_priv->buf;
|
||||
int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
|
||||
|
||||
/* If the array of pages is too small, grow it... */
|
||||
if (buf->page_array_size < pages_needed) {
|
||||
|
@ -911,7 +906,9 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
|
|||
wait_for_completion(&buf->completion);
|
||||
|
||||
cancel_delayed_work_sync(&fw_priv->timeout_work);
|
||||
if (!buf->data)
|
||||
if (is_fw_load_aborted(buf))
|
||||
retval = -EAGAIN;
|
||||
else if (!buf->data)
|
||||
retval = -ENOMEM;
|
||||
|
||||
device_remove_file(f_dev, &dev_attr_loading);
|
||||
|
@ -1111,10 +1108,11 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
|||
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv);
|
||||
if (ret) {
|
||||
if (opt_flags & FW_OPT_FALLBACK) {
|
||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||
dev_warn(device,
|
||||
"Direct firmware load failed with error %d\n",
|
||||
ret);
|
||||
"Direct firmware load for %s failed with error %d\n",
|
||||
name, ret);
|
||||
if (opt_flags & FW_OPT_USERHELPER) {
|
||||
dev_warn(device, "Falling back to user helper\n");
|
||||
ret = fw_load_from_user_helper(fw, name, device,
|
||||
opt_flags, timeout);
|
||||
|
@ -1171,7 +1169,6 @@ request_firmware(const struct firmware **firmware_p, const char *name,
|
|||
}
|
||||
EXPORT_SYMBOL(request_firmware);
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
/**
|
||||
* request_firmware: - load firmware directly without usermode helper
|
||||
* @firmware_p: pointer to firmware image
|
||||
|
@ -1188,12 +1185,12 @@ int request_firmware_direct(const struct firmware **firmware_p,
|
|||
{
|
||||
int ret;
|
||||
__module_get(THIS_MODULE);
|
||||
ret = _request_firmware(firmware_p, name, device, FW_OPT_UEVENT);
|
||||
ret = _request_firmware(firmware_p, name, device,
|
||||
FW_OPT_UEVENT | FW_OPT_NO_WARN);
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(request_firmware_direct);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* release_firmware: - release the resource associated with a firmware image
|
||||
|
@ -1277,7 +1274,7 @@ request_firmware_nowait(
|
|||
fw_work->context = context;
|
||||
fw_work->cont = cont;
|
||||
fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
|
||||
(uevent ? FW_OPT_UEVENT : 0);
|
||||
(uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
|
||||
|
||||
if (!try_module_get(module)) {
|
||||
kfree(fw_work);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/idr.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk/clk-conf.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
|
@ -176,7 +177,7 @@ EXPORT_SYMBOL_GPL(platform_add_devices);
|
|||
|
||||
struct platform_object {
|
||||
struct platform_device pdev;
|
||||
char name[1];
|
||||
char name[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -202,6 +203,7 @@ static void platform_device_release(struct device *dev)
|
|||
kfree(pa->pdev.dev.platform_data);
|
||||
kfree(pa->pdev.mfd_cell);
|
||||
kfree(pa->pdev.resource);
|
||||
kfree(pa->pdev.driver_override);
|
||||
kfree(pa);
|
||||
}
|
||||
|
||||
|
@ -217,7 +219,7 @@ struct platform_device *platform_device_alloc(const char *name, int id)
|
|||
{
|
||||
struct platform_object *pa;
|
||||
|
||||
pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
|
||||
pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL);
|
||||
if (pa) {
|
||||
strcpy(pa->name, name);
|
||||
pa->pdev.name = pa->name;
|
||||
|
@ -713,8 +715,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
|
|||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static ssize_t driver_override_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
char *driver_override, *old = pdev->driver_override, *cp;
|
||||
|
||||
if (count > PATH_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
driver_override = kstrndup(buf, count, GFP_KERNEL);
|
||||
if (!driver_override)
|
||||
return -ENOMEM;
|
||||
|
||||
cp = strchr(driver_override, '\n');
|
||||
if (cp)
|
||||
*cp = '\0';
|
||||
|
||||
if (strlen(driver_override)) {
|
||||
pdev->driver_override = driver_override;
|
||||
} else {
|
||||
kfree(driver_override);
|
||||
pdev->driver_override = NULL;
|
||||
}
|
||||
|
||||
kfree(old);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t driver_override_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", pdev->driver_override);
|
||||
}
|
||||
static DEVICE_ATTR_RW(driver_override);
|
||||
|
||||
|
||||
static struct attribute *platform_dev_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
&dev_attr_driver_override.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(platform_dev);
|
||||
|
@ -770,6 +813,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)
|
|||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct platform_driver *pdrv = to_platform_driver(drv);
|
||||
|
||||
/* When driver_override is set, only bind to the matching driver */
|
||||
if (pdev->driver_override)
|
||||
return !strcmp(pdev->driver_override, drv->name);
|
||||
|
||||
/* Attempt an OF style match first */
|
||||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 Canonical Ltd
|
||||
*
|
||||
* Based on bo.c which bears the following copyright notice,
|
||||
* but is dual licensed:
|
||||
*
|
||||
* Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
**************************************************************************/
|
||||
/*
|
||||
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
||||
*/
|
||||
|
||||
#include <linux/reservation.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
DEFINE_WW_CLASS(reservation_ww_class);
|
||||
EXPORT_SYMBOL(reservation_ww_class);
|
|
@ -212,9 +212,9 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
|
|||
mutex_init(&gdev->lock);
|
||||
INIT_LIST_HEAD(&gdev->next);
|
||||
|
||||
gdev->base = devm_request_and_ioremap(&pdev->dev, r);
|
||||
if (!gdev->base)
|
||||
return -ENOMEM;
|
||||
gdev->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(gdev->base))
|
||||
return PTR_ERR(gdev->base);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, timeout_irq,
|
||||
brcmstb_gisb_timeout_handler, 0, pdev->name,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
obj-y := dma-buf.o fence.o reservation.o seqno-fence.o
|
|
@ -25,10 +25,13 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/fence.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/reservation.h>
|
||||
|
||||
static inline int is_dma_buf_file(struct file *);
|
||||
|
||||
|
@ -50,12 +53,25 @@ static int dma_buf_release(struct inode *inode, struct file *file)
|
|||
|
||||
BUG_ON(dmabuf->vmapping_counter);
|
||||
|
||||
/*
|
||||
* Any fences that a dma-buf poll can wait on should be signaled
|
||||
* before releasing dma-buf. This is the responsibility of each
|
||||
* driver that uses the reservation objects.
|
||||
*
|
||||
* If you hit this BUG() it means someone dropped their ref to the
|
||||
* dma-buf while still having pending operation to the buffer.
|
||||
*/
|
||||
BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active);
|
||||
|
||||
dmabuf->ops->release(dmabuf);
|
||||
|
||||
mutex_lock(&db_list.lock);
|
||||
list_del(&dmabuf->list_node);
|
||||
mutex_unlock(&db_list.lock);
|
||||
|
||||
if (dmabuf->resv == (struct reservation_object *)&dmabuf[1])
|
||||
reservation_object_fini(dmabuf->resv);
|
||||
|
||||
kfree(dmabuf);
|
||||
return 0;
|
||||
}
|
||||
|
@ -103,10 +119,141 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
|
|||
return base + offset;
|
||||
}
|
||||
|
||||
static void dma_buf_poll_cb(struct fence *fence, struct fence_cb *cb)
|
||||
{
|
||||
struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *)cb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dcb->poll->lock, flags);
|
||||
wake_up_locked_poll(dcb->poll, dcb->active);
|
||||
dcb->active = 0;
|
||||
spin_unlock_irqrestore(&dcb->poll->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
|
||||
{
|
||||
struct dma_buf *dmabuf;
|
||||
struct reservation_object *resv;
|
||||
struct reservation_object_list *fobj;
|
||||
struct fence *fence_excl;
|
||||
unsigned long events;
|
||||
unsigned shared_count, seq;
|
||||
|
||||
dmabuf = file->private_data;
|
||||
if (!dmabuf || !dmabuf->resv)
|
||||
return POLLERR;
|
||||
|
||||
resv = dmabuf->resv;
|
||||
|
||||
poll_wait(file, &dmabuf->poll, poll);
|
||||
|
||||
events = poll_requested_events(poll) & (POLLIN | POLLOUT);
|
||||
if (!events)
|
||||
return 0;
|
||||
|
||||
retry:
|
||||
seq = read_seqcount_begin(&resv->seq);
|
||||
rcu_read_lock();
|
||||
|
||||
fobj = rcu_dereference(resv->fence);
|
||||
if (fobj)
|
||||
shared_count = fobj->shared_count;
|
||||
else
|
||||
shared_count = 0;
|
||||
fence_excl = rcu_dereference(resv->fence_excl);
|
||||
if (read_seqcount_retry(&resv->seq, seq)) {
|
||||
rcu_read_unlock();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) {
|
||||
struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl;
|
||||
unsigned long pevents = POLLIN;
|
||||
|
||||
if (shared_count == 0)
|
||||
pevents |= POLLOUT;
|
||||
|
||||
spin_lock_irq(&dmabuf->poll.lock);
|
||||
if (dcb->active) {
|
||||
dcb->active |= pevents;
|
||||
events &= ~pevents;
|
||||
} else
|
||||
dcb->active = pevents;
|
||||
spin_unlock_irq(&dmabuf->poll.lock);
|
||||
|
||||
if (events & pevents) {
|
||||
if (!fence_get_rcu(fence_excl)) {
|
||||
/* force a recheck */
|
||||
events &= ~pevents;
|
||||
dma_buf_poll_cb(NULL, &dcb->cb);
|
||||
} else if (!fence_add_callback(fence_excl, &dcb->cb,
|
||||
dma_buf_poll_cb)) {
|
||||
events &= ~pevents;
|
||||
fence_put(fence_excl);
|
||||
} else {
|
||||
/*
|
||||
* No callback queued, wake up any additional
|
||||
* waiters.
|
||||
*/
|
||||
fence_put(fence_excl);
|
||||
dma_buf_poll_cb(NULL, &dcb->cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((events & POLLOUT) && shared_count > 0) {
|
||||
struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared;
|
||||
int i;
|
||||
|
||||
/* Only queue a new callback if no event has fired yet */
|
||||
spin_lock_irq(&dmabuf->poll.lock);
|
||||
if (dcb->active)
|
||||
events &= ~POLLOUT;
|
||||
else
|
||||
dcb->active = POLLOUT;
|
||||
spin_unlock_irq(&dmabuf->poll.lock);
|
||||
|
||||
if (!(events & POLLOUT))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < shared_count; ++i) {
|
||||
struct fence *fence = rcu_dereference(fobj->shared[i]);
|
||||
|
||||
if (!fence_get_rcu(fence)) {
|
||||
/*
|
||||
* fence refcount dropped to zero, this means
|
||||
* that fobj has been freed
|
||||
*
|
||||
* call dma_buf_poll_cb and force a recheck!
|
||||
*/
|
||||
events &= ~POLLOUT;
|
||||
dma_buf_poll_cb(NULL, &dcb->cb);
|
||||
break;
|
||||
}
|
||||
if (!fence_add_callback(fence, &dcb->cb,
|
||||
dma_buf_poll_cb)) {
|
||||
fence_put(fence);
|
||||
events &= ~POLLOUT;
|
||||
break;
|
||||
}
|
||||
fence_put(fence);
|
||||
}
|
||||
|
||||
/* No callback queued, wake up any additional waiters. */
|
||||
if (i == shared_count)
|
||||
dma_buf_poll_cb(NULL, &dcb->cb);
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return events;
|
||||
}
|
||||
|
||||
static const struct file_operations dma_buf_fops = {
|
||||
.release = dma_buf_release,
|
||||
.mmap = dma_buf_mmap_internal,
|
||||
.llseek = dma_buf_llseek,
|
||||
.poll = dma_buf_poll,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -128,6 +275,7 @@ static inline int is_dma_buf_file(struct file *file)
|
|||
* @size: [in] Size of the buffer
|
||||
* @flags: [in] mode flags for the file.
|
||||
* @exp_name: [in] name of the exporting module - useful for debugging.
|
||||
* @resv: [in] reservation-object, NULL to allocate default one.
|
||||
*
|
||||
* Returns, on success, a newly created dma_buf object, which wraps the
|
||||
* supplied private data and operations for dma_buf_ops. On either missing
|
||||
|
@ -135,10 +283,17 @@ static inline int is_dma_buf_file(struct file *file)
|
|||
*
|
||||
*/
|
||||
struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
|
||||
size_t size, int flags, const char *exp_name)
|
||||
size_t size, int flags, const char *exp_name,
|
||||
struct reservation_object *resv)
|
||||
{
|
||||
struct dma_buf *dmabuf;
|
||||
struct file *file;
|
||||
size_t alloc_size = sizeof(struct dma_buf);
|
||||
if (!resv)
|
||||
alloc_size += sizeof(struct reservation_object);
|
||||
else
|
||||
/* prevent &dma_buf[1] == dma_buf->resv */
|
||||
alloc_size += 1;
|
||||
|
||||
if (WARN_ON(!priv || !ops
|
||||
|| !ops->map_dma_buf
|
||||
|
@ -150,7 +305,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
|
||||
dmabuf = kzalloc(alloc_size, GFP_KERNEL);
|
||||
if (dmabuf == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -158,6 +313,15 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
|
|||
dmabuf->ops = ops;
|
||||
dmabuf->size = size;
|
||||
dmabuf->exp_name = exp_name;
|
||||
init_waitqueue_head(&dmabuf->poll);
|
||||
dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
|
||||
dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
|
||||
|
||||
if (!resv) {
|
||||
resv = (struct reservation_object *)&dmabuf[1];
|
||||
reservation_object_init(resv);
|
||||
}
|
||||
dmabuf->resv = resv;
|
||||
|
||||
file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);
|
||||
if (IS_ERR(file)) {
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* Fence mechanism for dma-buf and to allow for asynchronous dma access
|
||||
*
|
||||
* Copyright (C) 2012 Canonical Ltd
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/fence.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/fence.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL(fence_annotate_wait_on);
|
||||
EXPORT_TRACEPOINT_SYMBOL(fence_emit);
|
||||
|
||||
/**
|
||||
* fence context counter: each execution context should have its own
|
||||
* fence context, this allows checking if fences belong to the same
|
||||
* context or not. One device can have multiple separate contexts,
|
||||
* and they're used if some engine can run independently of another.
|
||||
*/
|
||||
static atomic_t fence_context_counter = ATOMIC_INIT(0);
|
||||
|
||||
/**
|
||||
* fence_context_alloc - allocate an array of fence contexts
|
||||
* @num: [in] amount of contexts to allocate
|
||||
*
|
||||
* This function will return the first index of the number of fences allocated.
|
||||
* The fence context is used for setting fence->context to a unique number.
|
||||
*/
|
||||
unsigned fence_context_alloc(unsigned num)
|
||||
{
|
||||
BUG_ON(!num);
|
||||
return atomic_add_return(num, &fence_context_counter) - num;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_context_alloc);
|
||||
|
||||
/**
|
||||
* fence_signal_locked - signal completion of a fence
|
||||
* @fence: the fence to signal
|
||||
*
|
||||
* Signal completion for software callbacks on a fence, this will unblock
|
||||
* fence_wait() calls and run all the callbacks added with
|
||||
* fence_add_callback(). Can be called multiple times, but since a fence
|
||||
* can only go from unsignaled to signaled state, it will only be effective
|
||||
* the first time.
|
||||
*
|
||||
* Unlike fence_signal, this function must be called with fence->lock held.
|
||||
*/
|
||||
int fence_signal_locked(struct fence *fence)
|
||||
{
|
||||
struct fence_cb *cur, *tmp;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(!fence))
|
||||
return -EINVAL;
|
||||
|
||||
if (!ktime_to_ns(fence->timestamp)) {
|
||||
fence->timestamp = ktime_get();
|
||||
smp_mb__before_atomic();
|
||||
}
|
||||
|
||||
if (test_and_set_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
|
||||
ret = -EINVAL;
|
||||
|
||||
/*
|
||||
* we might have raced with the unlocked fence_signal,
|
||||
* still run through all callbacks
|
||||
*/
|
||||
} else
|
||||
trace_fence_signaled(fence);
|
||||
|
||||
list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) {
|
||||
list_del_init(&cur->node);
|
||||
cur->func(fence, cur);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_signal_locked);
|
||||
|
||||
/**
|
||||
* fence_signal - signal completion of a fence
|
||||
* @fence: the fence to signal
|
||||
*
|
||||
* Signal completion for software callbacks on a fence, this will unblock
|
||||
* fence_wait() calls and run all the callbacks added with
|
||||
* fence_add_callback(). Can be called multiple times, but since a fence
|
||||
* can only go from unsignaled to signaled state, it will only be effective
|
||||
* the first time.
|
||||
*/
|
||||
int fence_signal(struct fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!fence)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ktime_to_ns(fence->timestamp)) {
|
||||
fence->timestamp = ktime_get();
|
||||
smp_mb__before_atomic();
|
||||
}
|
||||
|
||||
if (test_and_set_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
return -EINVAL;
|
||||
|
||||
trace_fence_signaled(fence);
|
||||
|
||||
if (test_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags)) {
|
||||
struct fence_cb *cur, *tmp;
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) {
|
||||
list_del_init(&cur->node);
|
||||
cur->func(fence, cur);
|
||||
}
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_signal);
|
||||
|
||||
/**
|
||||
* fence_wait_timeout - sleep until the fence gets signaled
|
||||
* or until timeout elapses
|
||||
* @fence: [in] the fence to wait on
|
||||
* @intr: [in] if true, do an interruptible wait
|
||||
* @timeout: [in] timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT
|
||||
*
|
||||
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or the
|
||||
* remaining timeout in jiffies on success. Other error values may be
|
||||
* returned on custom implementations.
|
||||
*
|
||||
* Performs a synchronous wait on this fence. It is assumed the caller
|
||||
* directly or indirectly (buf-mgr between reservation and committing)
|
||||
* holds a reference to the fence, otherwise the fence might be
|
||||
* freed before return, resulting in undefined behavior.
|
||||
*/
|
||||
signed long
|
||||
fence_wait_timeout(struct fence *fence, bool intr, signed long timeout)
|
||||
{
|
||||
signed long ret;
|
||||
|
||||
if (WARN_ON(timeout < 0))
|
||||
return -EINVAL;
|
||||
|
||||
trace_fence_wait_start(fence);
|
||||
ret = fence->ops->wait(fence, intr, timeout);
|
||||
trace_fence_wait_end(fence);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_wait_timeout);
|
||||
|
||||
void fence_release(struct kref *kref)
|
||||
{
|
||||
struct fence *fence =
|
||||
container_of(kref, struct fence, refcount);
|
||||
|
||||
trace_fence_destroy(fence);
|
||||
|
||||
BUG_ON(!list_empty(&fence->cb_list));
|
||||
|
||||
if (fence->ops->release)
|
||||
fence->ops->release(fence);
|
||||
else
|
||||
fence_free(fence);
|
||||
}
|
||||
EXPORT_SYMBOL(fence_release);
|
||||
|
||||
void fence_free(struct fence *fence)
|
||||
{
|
||||
kfree_rcu(fence, rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(fence_free);
|
||||
|
||||
/**
|
||||
* fence_enable_sw_signaling - enable signaling on fence
|
||||
* @fence: [in] the fence to enable
|
||||
*
|
||||
* this will request for sw signaling to be enabled, to make the fence
|
||||
* complete as soon as possible
|
||||
*/
|
||||
void fence_enable_sw_signaling(struct fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags) &&
|
||||
!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
|
||||
trace_fence_enable_signal(fence);
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
|
||||
if (!fence->ops->enable_signaling(fence))
|
||||
fence_signal_locked(fence);
|
||||
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fence_enable_sw_signaling);
|
||||
|
||||
/**
|
||||
* fence_add_callback - add a callback to be called when the fence
|
||||
* is signaled
|
||||
* @fence: [in] the fence to wait on
|
||||
* @cb: [in] the callback to register
|
||||
* @func: [in] the function to call
|
||||
*
|
||||
* cb will be initialized by fence_add_callback, no initialization
|
||||
* by the caller is required. Any number of callbacks can be registered
|
||||
* to a fence, but a callback can only be registered to one fence at a time.
|
||||
*
|
||||
* Note that the callback can be called from an atomic context. If
|
||||
* fence is already signaled, this function will return -ENOENT (and
|
||||
* *not* call the callback)
|
||||
*
|
||||
* Add a software callback to the fence. Same restrictions apply to
|
||||
* refcount as it does to fence_wait, however the caller doesn't need to
|
||||
* keep a refcount to fence afterwards: when software access is enabled,
|
||||
* the creator of the fence is required to keep the fence alive until
|
||||
* after it signals with fence_signal. The callback itself can be called
|
||||
* from irq context.
|
||||
*
|
||||
*/
|
||||
int fence_add_callback(struct fence *fence, struct fence_cb *cb,
|
||||
fence_func_t func)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
bool was_set;
|
||||
|
||||
if (WARN_ON(!fence || !func))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
|
||||
INIT_LIST_HEAD(&cb->node);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
|
||||
was_set = test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags);
|
||||
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
ret = -ENOENT;
|
||||
else if (!was_set) {
|
||||
trace_fence_enable_signal(fence);
|
||||
|
||||
if (!fence->ops->enable_signaling(fence)) {
|
||||
fence_signal_locked(fence);
|
||||
ret = -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
cb->func = func;
|
||||
list_add_tail(&cb->node, &fence->cb_list);
|
||||
} else
|
||||
INIT_LIST_HEAD(&cb->node);
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_add_callback);
|
||||
|
||||
/**
|
||||
* fence_remove_callback - remove a callback from the signaling list
|
||||
* @fence: [in] the fence to wait on
|
||||
* @cb: [in] the callback to remove
|
||||
*
|
||||
* Remove a previously queued callback from the fence. This function returns
|
||||
* true if the callback is succesfully removed, or false if the fence has
|
||||
* already been signaled.
|
||||
*
|
||||
* *WARNING*:
|
||||
* Cancelling a callback should only be done if you really know what you're
|
||||
* doing, since deadlocks and race conditions could occur all too easily. For
|
||||
* this reason, it should only ever be done on hardware lockup recovery,
|
||||
* with a reference held to the fence.
|
||||
*/
|
||||
bool
|
||||
fence_remove_callback(struct fence *fence, struct fence_cb *cb)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool ret;
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
|
||||
ret = !list_empty(&cb->node);
|
||||
if (ret)
|
||||
list_del_init(&cb->node);
|
||||
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_remove_callback);
|
||||
|
||||
struct default_wait_cb {
|
||||
struct fence_cb base;
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
static void
|
||||
fence_default_wait_cb(struct fence *fence, struct fence_cb *cb)
|
||||
{
|
||||
struct default_wait_cb *wait =
|
||||
container_of(cb, struct default_wait_cb, base);
|
||||
|
||||
wake_up_state(wait->task, TASK_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* fence_default_wait - default sleep until the fence gets signaled
|
||||
* or until timeout elapses
|
||||
* @fence: [in] the fence to wait on
|
||||
* @intr: [in] if true, do an interruptible wait
|
||||
* @timeout: [in] timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT
|
||||
*
|
||||
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or the
|
||||
* remaining timeout in jiffies on success.
|
||||
*/
|
||||
signed long
|
||||
fence_default_wait(struct fence *fence, bool intr, signed long timeout)
|
||||
{
|
||||
struct default_wait_cb cb;
|
||||
unsigned long flags;
|
||||
signed long ret = timeout;
|
||||
bool was_set;
|
||||
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
return timeout;
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
|
||||
if (intr && signal_pending(current)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
was_set = test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags);
|
||||
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
goto out;
|
||||
|
||||
if (!was_set) {
|
||||
trace_fence_enable_signal(fence);
|
||||
|
||||
if (!fence->ops->enable_signaling(fence)) {
|
||||
fence_signal_locked(fence);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cb.base.func = fence_default_wait_cb;
|
||||
cb.task = current;
|
||||
list_add(&cb.base.node, &fence->cb_list);
|
||||
|
||||
while (!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) {
|
||||
if (intr)
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
else
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
|
||||
ret = schedule_timeout(ret);
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
if (ret > 0 && intr && signal_pending(current))
|
||||
ret = -ERESTARTSYS;
|
||||
}
|
||||
|
||||
if (!list_empty(&cb.base.node))
|
||||
list_del(&cb.base.node);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fence_default_wait);
|
||||
|
||||
/**
|
||||
* fence_init - Initialize a custom fence.
|
||||
* @fence: [in] the fence to initialize
|
||||
* @ops: [in] the fence_ops for operations on this fence
|
||||
* @lock: [in] the irqsafe spinlock to use for locking this fence
|
||||
* @context: [in] the execution context this fence is run on
|
||||
* @seqno: [in] a linear increasing sequence number for this context
|
||||
*
|
||||
* Initializes an allocated fence, the caller doesn't have to keep its
|
||||
* refcount after committing with this fence, but it will need to hold a
|
||||
* refcount again if fence_ops.enable_signaling gets called. This can
|
||||
* be used for other implementing other types of fence.
|
||||
*
|
||||
* context and seqno are used for easy comparison between fences, allowing
|
||||
* to check which fence is later by simply using fence_later.
|
||||
*/
|
||||
void
|
||||
fence_init(struct fence *fence, const struct fence_ops *ops,
|
||||
spinlock_t *lock, unsigned context, unsigned seqno)
|
||||
{
|
||||
BUG_ON(!lock);
|
||||
BUG_ON(!ops || !ops->wait || !ops->enable_signaling ||
|
||||
!ops->get_driver_name || !ops->get_timeline_name);
|
||||
|
||||
kref_init(&fence->refcount);
|
||||
fence->ops = ops;
|
||||
INIT_LIST_HEAD(&fence->cb_list);
|
||||
fence->lock = lock;
|
||||
fence->context = context;
|
||||
fence->seqno = seqno;
|
||||
fence->flags = 0UL;
|
||||
|
||||
trace_fence_init(fence);
|
||||
}
|
||||
EXPORT_SYMBOL(fence_init);
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 Canonical Ltd (Maarten Lankhorst)
|
||||
*
|
||||
* Based on bo.c which bears the following copyright notice,
|
||||
* but is dual licensed:
|
||||
*
|
||||
* Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
**************************************************************************/
|
||||
/*
|
||||
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
||||
*/
|
||||
|
||||
#include <linux/reservation.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
DEFINE_WW_CLASS(reservation_ww_class);
|
||||
EXPORT_SYMBOL(reservation_ww_class);
|
||||
|
||||
struct lock_class_key reservation_seqcount_class;
|
||||
EXPORT_SYMBOL(reservation_seqcount_class);
|
||||
|
||||
const char reservation_seqcount_string[] = "reservation_seqcount";
|
||||
EXPORT_SYMBOL(reservation_seqcount_string);
|
||||
/*
|
||||
* Reserve space to add a shared fence to a reservation_object,
|
||||
* must be called with obj->lock held.
|
||||
*/
|
||||
int reservation_object_reserve_shared(struct reservation_object *obj)
|
||||
{
|
||||
struct reservation_object_list *fobj, *old;
|
||||
u32 max;
|
||||
|
||||
old = reservation_object_get_list(obj);
|
||||
|
||||
if (old && old->shared_max) {
|
||||
if (old->shared_count < old->shared_max) {
|
||||
/* perform an in-place update */
|
||||
kfree(obj->staged);
|
||||
obj->staged = NULL;
|
||||
return 0;
|
||||
} else
|
||||
max = old->shared_max * 2;
|
||||
} else
|
||||
max = 4;
|
||||
|
||||
/*
|
||||
* resize obj->staged or allocate if it doesn't exist,
|
||||
* noop if already correct size
|
||||
*/
|
||||
fobj = krealloc(obj->staged, offsetof(typeof(*fobj), shared[max]),
|
||||
GFP_KERNEL);
|
||||
if (!fobj)
|
||||
return -ENOMEM;
|
||||
|
||||
obj->staged = fobj;
|
||||
fobj->shared_max = max;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(reservation_object_reserve_shared);
|
||||
|
||||
static void
|
||||
reservation_object_add_shared_inplace(struct reservation_object *obj,
|
||||
struct reservation_object_list *fobj,
|
||||
struct fence *fence)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
fence_get(fence);
|
||||
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&obj->seq);
|
||||
|
||||
for (i = 0; i < fobj->shared_count; ++i) {
|
||||
struct fence *old_fence;
|
||||
|
||||
old_fence = rcu_dereference_protected(fobj->shared[i],
|
||||
reservation_object_held(obj));
|
||||
|
||||
if (old_fence->context == fence->context) {
|
||||
/* memory barrier is added by write_seqcount_begin */
|
||||
RCU_INIT_POINTER(fobj->shared[i], fence);
|
||||
write_seqcount_end(&obj->seq);
|
||||
preempt_enable();
|
||||
|
||||
fence_put(old_fence);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* memory barrier is added by write_seqcount_begin,
|
||||
* fobj->shared_count is protected by this lock too
|
||||
*/
|
||||
RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
|
||||
fobj->shared_count++;
|
||||
|
||||
write_seqcount_end(&obj->seq);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static void
|
||||
reservation_object_add_shared_replace(struct reservation_object *obj,
|
||||
struct reservation_object_list *old,
|
||||
struct reservation_object_list *fobj,
|
||||
struct fence *fence)
|
||||
{
|
||||
unsigned i;
|
||||
struct fence *old_fence = NULL;
|
||||
|
||||
fence_get(fence);
|
||||
|
||||
if (!old) {
|
||||
RCU_INIT_POINTER(fobj->shared[0], fence);
|
||||
fobj->shared_count = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* no need to bump fence refcounts, rcu_read access
|
||||
* requires the use of kref_get_unless_zero, and the
|
||||
* references from the old struct are carried over to
|
||||
* the new.
|
||||
*/
|
||||
fobj->shared_count = old->shared_count;
|
||||
|
||||
for (i = 0; i < old->shared_count; ++i) {
|
||||
struct fence *check;
|
||||
|
||||
check = rcu_dereference_protected(old->shared[i],
|
||||
reservation_object_held(obj));
|
||||
|
||||
if (!old_fence && check->context == fence->context) {
|
||||
old_fence = check;
|
||||
RCU_INIT_POINTER(fobj->shared[i], fence);
|
||||
} else
|
||||
RCU_INIT_POINTER(fobj->shared[i], check);
|
||||
}
|
||||
if (!old_fence) {
|
||||
RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
|
||||
fobj->shared_count++;
|
||||
}
|
||||
|
||||
done:
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&obj->seq);
|
||||
/*
|
||||
* RCU_INIT_POINTER can be used here,
|
||||
* seqcount provides the necessary barriers
|
||||
*/
|
||||
RCU_INIT_POINTER(obj->fence, fobj);
|
||||
write_seqcount_end(&obj->seq);
|
||||
preempt_enable();
|
||||
|
||||
if (old)
|
||||
kfree_rcu(old, rcu);
|
||||
|
||||
if (old_fence)
|
||||
fence_put(old_fence);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a fence to a shared slot, obj->lock must be held, and
|
||||
* reservation_object_reserve_shared_fence has been called.
|
||||
*/
|
||||
void reservation_object_add_shared_fence(struct reservation_object *obj,
|
||||
struct fence *fence)
|
||||
{
|
||||
struct reservation_object_list *old, *fobj = obj->staged;
|
||||
|
||||
old = reservation_object_get_list(obj);
|
||||
obj->staged = NULL;
|
||||
|
||||
if (!fobj) {
|
||||
BUG_ON(old->shared_count >= old->shared_max);
|
||||
reservation_object_add_shared_inplace(obj, old, fence);
|
||||
} else
|
||||
reservation_object_add_shared_replace(obj, old, fobj, fence);
|
||||
}
|
||||
EXPORT_SYMBOL(reservation_object_add_shared_fence);
|
||||
|
||||
void reservation_object_add_excl_fence(struct reservation_object *obj,
|
||||
struct fence *fence)
|
||||
{
|
||||
struct fence *old_fence = reservation_object_get_excl(obj);
|
||||
struct reservation_object_list *old;
|
||||
u32 i = 0;
|
||||
|
||||
old = reservation_object_get_list(obj);
|
||||
if (old)
|
||||
i = old->shared_count;
|
||||
|
||||
if (fence)
|
||||
fence_get(fence);
|
||||
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&obj->seq);
|
||||
/* write_seqcount_begin provides the necessary memory barrier */
|
||||
RCU_INIT_POINTER(obj->fence_excl, fence);
|
||||
if (old)
|
||||
old->shared_count = 0;
|
||||
write_seqcount_end(&obj->seq);
|
||||
preempt_enable();
|
||||
|
||||
/* inplace update, no shared fences */
|
||||
while (i--)
|
||||
fence_put(rcu_dereference_protected(old->shared[i],
|
||||
reservation_object_held(obj)));
|
||||
|
||||
if (old_fence)
|
||||
fence_put(old_fence);
|
||||
}
|
||||
EXPORT_SYMBOL(reservation_object_add_excl_fence);
|
||||
|
||||
int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
||||
struct fence **pfence_excl,
|
||||
unsigned *pshared_count,
|
||||
struct fence ***pshared)
|
||||
{
|
||||
unsigned shared_count = 0;
|
||||
unsigned retry = 1;
|
||||
struct fence **shared = NULL, *fence_excl = NULL;
|
||||
int ret = 0;
|
||||
|
||||
while (retry) {
|
||||
struct reservation_object_list *fobj;
|
||||
unsigned seq;
|
||||
|
||||
seq = read_seqcount_begin(&obj->seq);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
fobj = rcu_dereference(obj->fence);
|
||||
if (fobj) {
|
||||
struct fence **nshared;
|
||||
size_t sz = sizeof(*shared) * fobj->shared_max;
|
||||
|
||||
nshared = krealloc(shared, sz,
|
||||
GFP_NOWAIT | __GFP_NOWARN);
|
||||
if (!nshared) {
|
||||
rcu_read_unlock();
|
||||
nshared = krealloc(shared, sz, GFP_KERNEL);
|
||||
if (nshared) {
|
||||
shared = nshared;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
shared_count = 0;
|
||||
break;
|
||||
}
|
||||
shared = nshared;
|
||||
memcpy(shared, fobj->shared, sz);
|
||||
shared_count = fobj->shared_count;
|
||||
} else
|
||||
shared_count = 0;
|
||||
fence_excl = rcu_dereference(obj->fence_excl);
|
||||
|
||||
retry = read_seqcount_retry(&obj->seq, seq);
|
||||
if (retry)
|
||||
goto unlock;
|
||||
|
||||
if (!fence_excl || fence_get_rcu(fence_excl)) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < shared_count; ++i) {
|
||||
if (fence_get_rcu(shared[i]))
|
||||
continue;
|
||||
|
||||
/* uh oh, refcount failed, abort and retry */
|
||||
while (i--)
|
||||
fence_put(shared[i]);
|
||||
|
||||
if (fence_excl) {
|
||||
fence_put(fence_excl);
|
||||
fence_excl = NULL;
|
||||
}
|
||||
|
||||
retry = 1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
retry = 1;
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
*pshared_count = shared_count;
|
||||
if (shared_count)
|
||||
*pshared = shared;
|
||||
else {
|
||||
*pshared = NULL;
|
||||
kfree(shared);
|
||||
}
|
||||
*pfence_excl = fence_excl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reservation_object_get_fences_rcu);
|
||||
|
||||
long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
|
||||
bool wait_all, bool intr,
|
||||
unsigned long timeout)
|
||||
{
|
||||
struct fence *fence;
|
||||
unsigned seq, shared_count, i = 0;
|
||||
long ret = timeout;
|
||||
|
||||
retry:
|
||||
fence = NULL;
|
||||
shared_count = 0;
|
||||
seq = read_seqcount_begin(&obj->seq);
|
||||
rcu_read_lock();
|
||||
|
||||
if (wait_all) {
|
||||
struct reservation_object_list *fobj = rcu_dereference(obj->fence);
|
||||
|
||||
if (fobj)
|
||||
shared_count = fobj->shared_count;
|
||||
|
||||
if (read_seqcount_retry(&obj->seq, seq))
|
||||
goto unlock_retry;
|
||||
|
||||
for (i = 0; i < shared_count; ++i) {
|
||||
struct fence *lfence = rcu_dereference(fobj->shared[i]);
|
||||
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags))
|
||||
continue;
|
||||
|
||||
if (!fence_get_rcu(lfence))
|
||||
goto unlock_retry;
|
||||
|
||||
if (fence_is_signaled(lfence)) {
|
||||
fence_put(lfence);
|
||||
continue;
|
||||
}
|
||||
|
||||
fence = lfence;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shared_count) {
|
||||
struct fence *fence_excl = rcu_dereference(obj->fence_excl);
|
||||
|
||||
if (read_seqcount_retry(&obj->seq, seq))
|
||||
goto unlock_retry;
|
||||
|
||||
if (fence_excl &&
|
||||
!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence_excl->flags)) {
|
||||
if (!fence_get_rcu(fence_excl))
|
||||
goto unlock_retry;
|
||||
|
||||
if (fence_is_signaled(fence_excl))
|
||||
fence_put(fence_excl);
|
||||
else
|
||||
fence = fence_excl;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
if (fence) {
|
||||
ret = fence_wait_timeout(fence, intr, ret);
|
||||
fence_put(fence);
|
||||
if (ret > 0 && wait_all && (i + 1 < shared_count))
|
||||
goto retry;
|
||||
}
|
||||
return ret;
|
||||
|
||||
unlock_retry:
|
||||
rcu_read_unlock();
|
||||
goto retry;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reservation_object_wait_timeout_rcu);
|
||||
|
||||
|
||||
static inline int
|
||||
reservation_object_test_signaled_single(struct fence *passed_fence)
|
||||
{
|
||||
struct fence *fence, *lfence = passed_fence;
|
||||
int ret = 1;
|
||||
|
||||
if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) {
|
||||
int ret;
|
||||
|
||||
fence = fence_get_rcu(lfence);
|
||||
if (!fence)
|
||||
return -1;
|
||||
|
||||
ret = !!fence_is_signaled(fence);
|
||||
fence_put(fence);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
|
||||
bool test_all)
|
||||
{
|
||||
unsigned seq, shared_count;
|
||||
int ret = true;
|
||||
|
||||
retry:
|
||||
shared_count = 0;
|
||||
seq = read_seqcount_begin(&obj->seq);
|
||||
rcu_read_lock();
|
||||
|
||||
if (test_all) {
|
||||
unsigned i;
|
||||
|
||||
struct reservation_object_list *fobj = rcu_dereference(obj->fence);
|
||||
|
||||
if (fobj)
|
||||
shared_count = fobj->shared_count;
|
||||
|
||||
if (read_seqcount_retry(&obj->seq, seq))
|
||||
goto unlock_retry;
|
||||
|
||||
for (i = 0; i < shared_count; ++i) {
|
||||
struct fence *fence = rcu_dereference(fobj->shared[i]);
|
||||
|
||||
ret = reservation_object_test_signaled_single(fence);
|
||||
if (ret < 0)
|
||||
goto unlock_retry;
|
||||
else if (!ret)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* There could be a read_seqcount_retry here, but nothing cares
|
||||
* about whether it's the old or newer fence pointers that are
|
||||
* signaled. That race could still have happened after checking
|
||||
* read_seqcount_retry. If you care, use ww_mutex_lock.
|
||||
*/
|
||||
}
|
||||
|
||||
if (!shared_count) {
|
||||
struct fence *fence_excl = rcu_dereference(obj->fence_excl);
|
||||
|
||||
if (read_seqcount_retry(&obj->seq, seq))
|
||||
goto unlock_retry;
|
||||
|
||||
if (fence_excl) {
|
||||
ret = reservation_object_test_signaled_single(fence_excl);
|
||||
if (ret < 0)
|
||||
goto unlock_retry;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
|
||||
unlock_retry:
|
||||
rcu_read_unlock();
|
||||
goto retry;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reservation_object_test_signaled_rcu);
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* seqno-fence, using a dma-buf to synchronize fencing
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Copyright (C) 2012-2014 Canonical Ltd
|
||||
* Authors:
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/seqno-fence.h>
|
||||
|
||||
static const char *seqno_fence_get_driver_name(struct fence *fence)
|
||||
{
|
||||
struct seqno_fence *seqno_fence = to_seqno_fence(fence);
|
||||
return seqno_fence->ops->get_driver_name(fence);
|
||||
}
|
||||
|
||||
static const char *seqno_fence_get_timeline_name(struct fence *fence)
|
||||
{
|
||||
struct seqno_fence *seqno_fence = to_seqno_fence(fence);
|
||||
return seqno_fence->ops->get_timeline_name(fence);
|
||||
}
|
||||
|
||||
static bool seqno_enable_signaling(struct fence *fence)
|
||||
{
|
||||
struct seqno_fence *seqno_fence = to_seqno_fence(fence);
|
||||
return seqno_fence->ops->enable_signaling(fence);
|
||||
}
|
||||
|
||||
static bool seqno_signaled(struct fence *fence)
|
||||
{
|
||||
struct seqno_fence *seqno_fence = to_seqno_fence(fence);
|
||||
return seqno_fence->ops->signaled && seqno_fence->ops->signaled(fence);
|
||||
}
|
||||
|
||||
static void seqno_release(struct fence *fence)
|
||||
{
|
||||
struct seqno_fence *f = to_seqno_fence(fence);
|
||||
|
||||
dma_buf_put(f->sync_buf);
|
||||
if (f->ops->release)
|
||||
f->ops->release(fence);
|
||||
else
|
||||
fence_free(&f->base);
|
||||
}
|
||||
|
||||
static signed long seqno_wait(struct fence *fence, bool intr, signed long timeout)
|
||||
{
|
||||
struct seqno_fence *f = to_seqno_fence(fence);
|
||||
return f->ops->wait(fence, intr, timeout);
|
||||
}
|
||||
|
||||
const struct fence_ops seqno_fence_ops = {
|
||||
.get_driver_name = seqno_fence_get_driver_name,
|
||||
.get_timeline_name = seqno_fence_get_timeline_name,
|
||||
.enable_signaling = seqno_enable_signaling,
|
||||
.signaled = seqno_signaled,
|
||||
.wait = seqno_wait,
|
||||
.release = seqno_release,
|
||||
};
|
||||
EXPORT_SYMBOL(seqno_fence_ops);
|
|
@ -1039,11 +1039,9 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
base = devm_request_and_ioremap(dev->dev, res);
|
||||
if (!base) {
|
||||
DRM_ERROR("failed to ioremap register\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
base = devm_ioremap_resource(dev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
|
||||
if (!dcrtc) {
|
||||
|
|
|
@ -539,7 +539,7 @@ armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
|
|||
int flags)
|
||||
{
|
||||
return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
|
||||
O_RDWR);
|
||||
O_RDWR, NULL);
|
||||
}
|
||||
|
||||
struct drm_gem_object *
|
||||
|
|
|
@ -336,7 +336,13 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
|
|||
struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, flags);
|
||||
struct reservation_object *robj = NULL;
|
||||
|
||||
if (dev->driver->gem_prime_res_obj)
|
||||
robj = dev->driver->gem_prime_res_obj(obj);
|
||||
|
||||
return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size,
|
||||
flags, robj);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_prime_export);
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,
|
|||
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
|
||||
|
||||
return dma_buf_export(obj, &exynos_dmabuf_ops,
|
||||
exynos_gem_obj->base.size, flags);
|
||||
exynos_gem_obj->base.size, flags, NULL);
|
||||
}
|
||||
|
||||
struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
|
||||
|
|
|
@ -237,7 +237,8 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags);
|
||||
return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj)
|
||||
|
|
|
@ -845,6 +845,7 @@ driver = {
|
|||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_pin = nouveau_gem_prime_pin,
|
||||
.gem_prime_res_obj = nouveau_gem_prime_res_obj,
|
||||
.gem_prime_unpin = nouveau_gem_prime_unpin,
|
||||
.gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,
|
||||
|
|
|
@ -35,6 +35,7 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
|
|||
struct drm_file *);
|
||||
|
||||
extern int nouveau_gem_prime_pin(struct drm_gem_object *);
|
||||
struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *);
|
||||
extern void nouveau_gem_prime_unpin(struct drm_gem_object *);
|
||||
extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *);
|
||||
extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(
|
||||
|
|
|
@ -102,3 +102,10 @@ void nouveau_gem_prime_unpin(struct drm_gem_object *obj)
|
|||
|
||||
nouveau_bo_unpin(nvbo);
|
||||
}
|
||||
|
||||
struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *obj)
|
||||
{
|
||||
struct nouveau_bo *nvbo = nouveau_gem_object(obj);
|
||||
|
||||
return nvbo->bo.resv;
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ static struct dma_buf_ops omap_dmabuf_ops = {
|
|||
struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags);
|
||||
return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags, NULL);
|
||||
}
|
||||
|
||||
struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
|
||||
|
|
|
@ -132,6 +132,7 @@ struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
|
|||
struct sg_table *sg);
|
||||
int radeon_gem_prime_pin(struct drm_gem_object *obj);
|
||||
void radeon_gem_prime_unpin(struct drm_gem_object *obj);
|
||||
struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *);
|
||||
void *radeon_gem_prime_vmap(struct drm_gem_object *obj);
|
||||
void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
|
||||
extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
|
@ -566,6 +567,7 @@ static struct drm_driver kms_driver = {
|
|||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_pin = radeon_gem_prime_pin,
|
||||
.gem_prime_unpin = radeon_gem_prime_unpin,
|
||||
.gem_prime_res_obj = radeon_gem_prime_res_obj,
|
||||
.gem_prime_get_sg_table = radeon_gem_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = radeon_gem_prime_import_sg_table,
|
||||
.gem_prime_vmap = radeon_gem_prime_vmap,
|
||||
|
|
|
@ -103,3 +103,11 @@ void radeon_gem_prime_unpin(struct drm_gem_object *obj)
|
|||
radeon_bo_unpin(bo);
|
||||
radeon_bo_unreserve(bo);
|
||||
}
|
||||
|
||||
|
||||
struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj)
|
||||
{
|
||||
struct radeon_bo *bo = gem_to_radeon_bo(obj);
|
||||
|
||||
return bo->tbo.resv;
|
||||
}
|
||||
|
|
|
@ -420,7 +420,7 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
|
|||
int flags)
|
||||
{
|
||||
return dma_buf_export(gem, &tegra_gem_prime_dmabuf_ops, gem->size,
|
||||
flags);
|
||||
flags, NULL);
|
||||
}
|
||||
|
||||
struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
|
||||
|
|
|
@ -695,7 +695,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
|
|||
}
|
||||
|
||||
dma_buf = dma_buf_export(prime, &tdev->ops,
|
||||
prime->size, flags);
|
||||
prime->size, flags, NULL);
|
||||
if (IS_ERR(dma_buf)) {
|
||||
ret = PTR_ERR(dma_buf);
|
||||
ttm_mem_global_free(tdev->mem_glob,
|
||||
|
|
|
@ -404,7 +404,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
|
|||
if (WARN_ON(!buf->sgt_base))
|
||||
return NULL;
|
||||
|
||||
dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags);
|
||||
dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags, NULL);
|
||||
if (IS_ERR(dbuf))
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ config SYNC
|
|||
bool "Synchronization framework"
|
||||
default n
|
||||
select ANON_INODES
|
||||
select DMA_SHARED_BUFFER
|
||||
---help---
|
||||
This option enables the framework for synchronization between multiple
|
||||
drivers. Sync implementations can take advantage of hardware
|
||||
|
|
|
@ -9,5 +9,5 @@ obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
|||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
|
||||
obj-$(CONFIG_SYNC) += sync.o
|
||||
obj-$(CONFIG_SYNC) += sync.o sync_debug.o
|
||||
obj-$(CONFIG_SW_SYNC) += sw_sync.o
|
||||
|
|
|
@ -1120,7 +1120,8 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client,
|
|||
ion_buffer_get(buffer);
|
||||
mutex_unlock(&client->lock);
|
||||
|
||||
dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR);
|
||||
dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR,
|
||||
NULL);
|
||||
if (IS_ERR(dmabuf)) {
|
||||
ion_buffer_put(buffer);
|
||||
return dmabuf;
|
||||
|
|
|
@ -50,7 +50,7 @@ static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt)
|
|||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt;
|
||||
struct sw_sync_timeline *obj =
|
||||
(struct sw_sync_timeline *)sync_pt->parent;
|
||||
(struct sw_sync_timeline *)sync_pt_parent(sync_pt);
|
||||
|
||||
return (struct sync_pt *) sw_sync_pt_create(obj, pt->value);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)
|
|||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
|
||||
struct sw_sync_timeline *obj =
|
||||
(struct sw_sync_timeline *)sync_pt->parent;
|
||||
(struct sw_sync_timeline *)sync_pt_parent(sync_pt);
|
||||
|
||||
return sw_sync_cmp(obj->value, pt->value) >= 0;
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ static void sw_sync_pt_value_str(struct sync_pt *sync_pt,
|
|||
char *str, int size)
|
||||
{
|
||||
struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
|
||||
|
||||
snprintf(str, size, "%d", pt->value);
|
||||
}
|
||||
|
||||
|
@ -157,7 +156,6 @@ static int sw_sync_open(struct inode *inode, struct file *file)
|
|||
static int sw_sync_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sw_sync_timeline *obj = file->private_data;
|
||||
|
||||
sync_timeline_destroy(&obj->obj);
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,6 +19,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/fence.h>
|
||||
|
||||
#include "uapi/sync.h"
|
||||
|
||||
|
@ -40,8 +41,6 @@ struct sync_fence;
|
|||
* -1 if a will signal before b
|
||||
* @free_pt: called before sync_pt is freed
|
||||
* @release_obj: called before sync_timeline is freed
|
||||
* @print_obj: deprecated
|
||||
* @print_pt: deprecated
|
||||
* @fill_driver_data: write implementation specific driver data to data.
|
||||
* should return an error if there is not enough room
|
||||
* as specified by size. This information is returned
|
||||
|
@ -67,13 +66,6 @@ struct sync_timeline_ops {
|
|||
/* optional */
|
||||
void (*release_obj)(struct sync_timeline *sync_timeline);
|
||||
|
||||
/* deprecated */
|
||||
void (*print_obj)(struct seq_file *s,
|
||||
struct sync_timeline *sync_timeline);
|
||||
|
||||
/* deprecated */
|
||||
void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt);
|
||||
|
||||
/* optional */
|
||||
int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size);
|
||||
|
||||
|
@ -104,19 +96,21 @@ struct sync_timeline {
|
|||
|
||||
/* protected by child_list_lock */
|
||||
bool destroyed;
|
||||
int context, value;
|
||||
|
||||
struct list_head child_list_head;
|
||||
spinlock_t child_list_lock;
|
||||
|
||||
struct list_head active_list_head;
|
||||
spinlock_t active_list_lock;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct list_head sync_timeline_list;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_pt - sync point
|
||||
* @parent: sync_timeline to which this sync_pt belongs
|
||||
* @fence: base fence class
|
||||
* @child_list: membership in sync_timeline.child_list_head
|
||||
* @active_list: membership in sync_timeline.active_list_head
|
||||
* @signaled_list: membership in temporary signaled_list on stack
|
||||
|
@ -127,19 +121,22 @@ struct sync_timeline {
|
|||
* signaled or error.
|
||||
*/
|
||||
struct sync_pt {
|
||||
struct sync_timeline *parent;
|
||||
struct fence base;
|
||||
|
||||
struct list_head child_list;
|
||||
|
||||
struct list_head active_list;
|
||||
struct list_head signaled_list;
|
||||
};
|
||||
|
||||
struct sync_fence *fence;
|
||||
struct list_head pt_list;
|
||||
static inline struct sync_timeline *sync_pt_parent(struct sync_pt *pt)
|
||||
{
|
||||
return container_of(pt->base.lock, struct sync_timeline,
|
||||
child_list_lock);
|
||||
}
|
||||
|
||||
/* protected by parent->active_list_lock */
|
||||
int status;
|
||||
|
||||
ktime_t timestamp;
|
||||
struct sync_fence_cb {
|
||||
struct fence_cb cb;
|
||||
struct fence *sync_pt;
|
||||
struct sync_fence *fence;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -149,9 +146,7 @@ struct sync_pt {
|
|||
* @name: name of sync_fence. Useful for debugging
|
||||
* @pt_list_head: list of sync_pts in the fence. immutable once fence
|
||||
* is created
|
||||
* @waiter_list_head: list of asynchronous waiters on this fence
|
||||
* @waiter_list_lock: lock protecting @waiter_list_head and @status
|
||||
* @status: 1: signaled, 0:active, <0: error
|
||||
* @status: 0: signaled, >0:active, <0: error
|
||||
*
|
||||
* @wq: wait queue for fence signaling
|
||||
* @sync_fence_list: membership in global fence list
|
||||
|
@ -160,17 +155,15 @@ struct sync_fence {
|
|||
struct file *file;
|
||||
struct kref kref;
|
||||
char name[32];
|
||||
|
||||
/* this list is immutable once the fence is created */
|
||||
struct list_head pt_list_head;
|
||||
|
||||
struct list_head waiter_list_head;
|
||||
spinlock_t waiter_list_lock; /* also protects status */
|
||||
int status;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct list_head sync_fence_list;
|
||||
#endif
|
||||
int num_fences;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
atomic_t status;
|
||||
|
||||
struct list_head sync_fence_list;
|
||||
struct sync_fence_cb cbs[];
|
||||
};
|
||||
|
||||
struct sync_fence_waiter;
|
||||
|
@ -184,14 +177,14 @@ typedef void (*sync_callback_t)(struct sync_fence *fence,
|
|||
* @callback_data: pointer to pass to @callback
|
||||
*/
|
||||
struct sync_fence_waiter {
|
||||
struct list_head waiter_list;
|
||||
|
||||
sync_callback_t callback;
|
||||
wait_queue_t work;
|
||||
sync_callback_t callback;
|
||||
};
|
||||
|
||||
static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter,
|
||||
sync_callback_t callback)
|
||||
{
|
||||
INIT_LIST_HEAD(&waiter->work.task_list);
|
||||
waiter->callback = callback;
|
||||
}
|
||||
|
||||
|
@ -341,4 +334,22 @@ int sync_fence_cancel_async(struct sync_fence *fence,
|
|||
*/
|
||||
int sync_fence_wait(struct sync_fence *fence, long timeout);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
extern void sync_timeline_debug_add(struct sync_timeline *obj);
|
||||
extern void sync_timeline_debug_remove(struct sync_timeline *obj);
|
||||
extern void sync_fence_debug_add(struct sync_fence *fence);
|
||||
extern void sync_fence_debug_remove(struct sync_fence *fence);
|
||||
extern void sync_dump(void);
|
||||
|
||||
#else
|
||||
# define sync_timeline_debug_add(obj)
|
||||
# define sync_timeline_debug_remove(obj)
|
||||
# define sync_fence_debug_add(fence)
|
||||
# define sync_fence_debug_remove(fence)
|
||||
# define sync_dump()
|
||||
#endif
|
||||
int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode,
|
||||
int wake_flags, void *key);
|
||||
|
||||
#endif /* _LINUX_SYNC_H */
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* drivers/base/sync.c
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include "sync.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static LIST_HEAD(sync_timeline_list_head);
|
||||
static DEFINE_SPINLOCK(sync_timeline_list_lock);
|
||||
static LIST_HEAD(sync_fence_list_head);
|
||||
static DEFINE_SPINLOCK(sync_fence_list_lock);
|
||||
|
||||
void sync_timeline_debug_add(struct sync_timeline *obj)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sync_timeline_list_lock, flags);
|
||||
list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
|
||||
spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
|
||||
}
|
||||
|
||||
void sync_timeline_debug_remove(struct sync_timeline *obj)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sync_timeline_list_lock, flags);
|
||||
list_del(&obj->sync_timeline_list);
|
||||
spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
|
||||
}
|
||||
|
||||
void sync_fence_debug_add(struct sync_fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sync_fence_list_lock, flags);
|
||||
list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
|
||||
spin_unlock_irqrestore(&sync_fence_list_lock, flags);
|
||||
}
|
||||
|
||||
void sync_fence_debug_remove(struct sync_fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sync_fence_list_lock, flags);
|
||||
list_del(&fence->sync_fence_list);
|
||||
spin_unlock_irqrestore(&sync_fence_list_lock, flags);
|
||||
}
|
||||
|
||||
static const char *sync_status_str(int status)
|
||||
{
|
||||
if (status == 0)
|
||||
return "signaled";
|
||||
|
||||
if (status > 0)
|
||||
return "active";
|
||||
|
||||
return "error";
|
||||
}
|
||||
|
||||
static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
|
||||
{
|
||||
int status = 1;
|
||||
struct sync_timeline *parent = sync_pt_parent(pt);
|
||||
|
||||
if (fence_is_signaled_locked(&pt->base))
|
||||
status = pt->base.status;
|
||||
|
||||
seq_printf(s, " %s%spt %s",
|
||||
fence ? parent->name : "",
|
||||
fence ? "_" : "",
|
||||
sync_status_str(status));
|
||||
|
||||
if (status <= 0) {
|
||||
struct timeval tv = ktime_to_timeval(pt->base.timestamp);
|
||||
|
||||
seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
|
||||
}
|
||||
|
||||
if (parent->ops->timeline_value_str &&
|
||||
parent->ops->pt_value_str) {
|
||||
char value[64];
|
||||
|
||||
parent->ops->pt_value_str(pt, value, sizeof(value));
|
||||
seq_printf(s, ": %s", value);
|
||||
if (fence) {
|
||||
parent->ops->timeline_value_str(parent, value,
|
||||
sizeof(value));
|
||||
seq_printf(s, " / %s", value);
|
||||
}
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
|
||||
{
|
||||
struct list_head *pos;
|
||||
unsigned long flags;
|
||||
|
||||
seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
|
||||
|
||||
if (obj->ops->timeline_value_str) {
|
||||
char value[64];
|
||||
|
||||
obj->ops->timeline_value_str(obj, value, sizeof(value));
|
||||
seq_printf(s, ": %s", value);
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
|
||||
spin_lock_irqsave(&obj->child_list_lock, flags);
|
||||
list_for_each(pos, &obj->child_list_head) {
|
||||
struct sync_pt *pt =
|
||||
container_of(pos, struct sync_pt, child_list);
|
||||
sync_print_pt(s, pt, false);
|
||||
}
|
||||
spin_unlock_irqrestore(&obj->child_list_lock, flags);
|
||||
}
|
||||
|
||||
static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
|
||||
{
|
||||
wait_queue_t *pos;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
|
||||
sync_status_str(atomic_read(&fence->status)));
|
||||
|
||||
for (i = 0; i < fence->num_fences; ++i) {
|
||||
struct sync_pt *pt =
|
||||
container_of(fence->cbs[i].sync_pt,
|
||||
struct sync_pt, base);
|
||||
|
||||
sync_print_pt(s, pt, true);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&fence->wq.lock, flags);
|
||||
list_for_each_entry(pos, &fence->wq.task_list, task_list) {
|
||||
struct sync_fence_waiter *waiter;
|
||||
|
||||
if (pos->func != &sync_fence_wake_up_wq)
|
||||
continue;
|
||||
|
||||
waiter = container_of(pos, struct sync_fence_waiter, work);
|
||||
|
||||
seq_printf(s, "waiter %pF\n", waiter->callback);
|
||||
}
|
||||
spin_unlock_irqrestore(&fence->wq.lock, flags);
|
||||
}
|
||||
|
||||
static int sync_debugfs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *pos;
|
||||
|
||||
seq_puts(s, "objs:\n--------------\n");
|
||||
|
||||
spin_lock_irqsave(&sync_timeline_list_lock, flags);
|
||||
list_for_each(pos, &sync_timeline_list_head) {
|
||||
struct sync_timeline *obj =
|
||||
container_of(pos, struct sync_timeline,
|
||||
sync_timeline_list);
|
||||
|
||||
sync_print_obj(s, obj);
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
|
||||
|
||||
seq_puts(s, "fences:\n--------------\n");
|
||||
|
||||
spin_lock_irqsave(&sync_fence_list_lock, flags);
|
||||
list_for_each(pos, &sync_fence_list_head) {
|
||||
struct sync_fence *fence =
|
||||
container_of(pos, struct sync_fence, sync_fence_list);
|
||||
|
||||
sync_print_fence(s, fence);
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&sync_fence_list_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sync_debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, sync_debugfs_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations sync_debugfs_fops = {
|
||||
.open = sync_debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static __init int sync_debugfs_init(void)
|
||||
{
|
||||
debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(sync_debugfs_init);
|
||||
|
||||
#define DUMP_CHUNK 256
|
||||
static char sync_dump_buf[64 * 1024];
|
||||
void sync_dump(void)
|
||||
{
|
||||
struct seq_file s = {
|
||||
.buf = sync_dump_buf,
|
||||
.size = sizeof(sync_dump_buf) - 1,
|
||||
};
|
||||
int i;
|
||||
|
||||
sync_debugfs_show(&s, NULL);
|
||||
|
||||
for (i = 0; i < s.count; i += DUMP_CHUNK) {
|
||||
if ((s.count - i) > DUMP_CHUNK) {
|
||||
char c = s.buf[i + DUMP_CHUNK];
|
||||
|
||||
s.buf[i + DUMP_CHUNK] = 0;
|
||||
pr_cont("%s", s.buf + i);
|
||||
s.buf[i + DUMP_CHUNK] = c;
|
||||
} else {
|
||||
s.buf[s.count] = 0;
|
||||
pr_cont("%s", s.buf + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -45,7 +45,7 @@ TRACE_EVENT(sync_wait,
|
|||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, fence->name);
|
||||
__entry->status = fence->status;
|
||||
__entry->status = atomic_read(&fence->status);
|
||||
__entry->begin = begin;
|
||||
),
|
||||
|
||||
|
@ -54,19 +54,19 @@ TRACE_EVENT(sync_wait,
|
|||
);
|
||||
|
||||
TRACE_EVENT(sync_pt,
|
||||
TP_PROTO(struct sync_pt *pt),
|
||||
TP_PROTO(struct fence *pt),
|
||||
|
||||
TP_ARGS(pt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(timeline, pt->parent->name)
|
||||
__string(timeline, pt->ops->get_timeline_name(pt))
|
||||
__array(char, value, 32)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(timeline, pt->parent->name);
|
||||
if (pt->parent->ops->pt_value_str) {
|
||||
pt->parent->ops->pt_value_str(pt, __entry->value,
|
||||
__assign_str(timeline, pt->ops->get_timeline_name(pt));
|
||||
if (pt->ops->fence_value_str) {
|
||||
pt->ops->fence_value_str(pt, __entry->value,
|
||||
sizeof(__entry->value));
|
||||
} else {
|
||||
__entry->value[0] = '\0';
|
||||
|
|
|
@ -451,7 +451,7 @@ static ssize_t read_file_bool(struct file *file, char __user *user_buf,
|
|||
{
|
||||
char buf[3];
|
||||
u32 *val = file->private_data;
|
||||
|
||||
|
||||
if (*val)
|
||||
buf[0] = 'Y';
|
||||
else
|
||||
|
|
|
@ -66,7 +66,7 @@ static struct inode *debugfs_get_inode(struct super_block *sb, umode_t mode, dev
|
|||
break;
|
||||
}
|
||||
}
|
||||
return inode;
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* SMP-safe */
|
||||
|
@ -317,7 +317,7 @@ static struct dentry *__create_file(const char *name, umode_t mode,
|
|||
goto exit;
|
||||
|
||||
/* If the parent is not specified, we create it in the root.
|
||||
* We need the root dentry to do this, which is in the super
|
||||
* We need the root dentry to do this, which is in the super
|
||||
* block. A pointer to that is in the struct vfsmount that we
|
||||
* have around.
|
||||
*/
|
||||
|
@ -330,7 +330,7 @@ static struct dentry *__create_file(const char *name, umode_t mode,
|
|||
switch (mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
error = debugfs_mkdir(parent->d_inode, dentry, mode);
|
||||
|
||||
|
||||
break;
|
||||
case S_IFLNK:
|
||||
error = debugfs_link(parent->d_inode, dentry, mode,
|
||||
|
@ -534,7 +534,7 @@ EXPORT_SYMBOL_GPL(debugfs_remove);
|
|||
*/
|
||||
void debugfs_remove_recursive(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *child, *next, *parent;
|
||||
struct dentry *child, *parent;
|
||||
|
||||
if (IS_ERR_OR_NULL(dentry))
|
||||
return;
|
||||
|
@ -546,30 +546,49 @@ void debugfs_remove_recursive(struct dentry *dentry)
|
|||
parent = dentry;
|
||||
down:
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
list_for_each_entry_safe(child, next, &parent->d_subdirs, d_u.d_child) {
|
||||
loop:
|
||||
/*
|
||||
* The parent->d_subdirs is protected by the d_lock. Outside that
|
||||
* lock, the child can be unlinked and set to be freed which can
|
||||
* use the d_u.d_child as the rcu head and corrupt this list.
|
||||
*/
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) {
|
||||
if (!debugfs_positive(child))
|
||||
continue;
|
||||
|
||||
/* perhaps simple_empty(child) makes more sense */
|
||||
if (!list_empty(&child->d_subdirs)) {
|
||||
spin_unlock(&parent->d_lock);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
parent = child;
|
||||
goto down;
|
||||
}
|
||||
up:
|
||||
|
||||
spin_unlock(&parent->d_lock);
|
||||
|
||||
if (!__debugfs_remove(child, parent))
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
|
||||
/*
|
||||
* The parent->d_lock protects agaist child from unlinking
|
||||
* from d_subdirs. When releasing the parent->d_lock we can
|
||||
* no longer trust that the next pointer is valid.
|
||||
* Restart the loop. We'll skip this one with the
|
||||
* debugfs_positive() check.
|
||||
*/
|
||||
goto loop;
|
||||
}
|
||||
spin_unlock(&parent->d_lock);
|
||||
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
child = parent;
|
||||
parent = parent->d_parent;
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
|
||||
if (child != dentry) {
|
||||
next = list_next_entry(child, d_u.d_child);
|
||||
goto up;
|
||||
}
|
||||
if (child != dentry)
|
||||
/* go up */
|
||||
goto loop;
|
||||
|
||||
if (!__debugfs_remove(child, parent))
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
|
|
|
@ -896,7 +896,7 @@ const struct file_operations kernfs_file_fops = {
|
|||
* @ops: kernfs operations for the file
|
||||
* @priv: private data for the file
|
||||
* @ns: optional namespace tag of the file
|
||||
* @static_name: don't copy file name
|
||||
* @name_is_static: don't copy file name
|
||||
* @key: lockdep key for the file's active_ref, %NULL to disable lockdep
|
||||
*
|
||||
* Returns the created node on success, ERR_PTR() value on error.
|
||||
|
|
|
@ -83,6 +83,7 @@ struct drm_device;
|
|||
|
||||
struct device_node;
|
||||
struct videomode;
|
||||
struct reservation_object;
|
||||
|
||||
#include <drm/drm_os_linux.h>
|
||||
#include <drm/drm_hashtab.h>
|
||||
|
@ -923,6 +924,8 @@ struct drm_driver {
|
|||
/* low-level interface used by drm_gem_prime_{import,export} */
|
||||
int (*gem_prime_pin)(struct drm_gem_object *obj);
|
||||
void (*gem_prime_unpin)(struct drm_gem_object *obj);
|
||||
struct reservation_object * (*gem_prime_res_obj)(
|
||||
struct drm_gem_object *obj);
|
||||
struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj);
|
||||
struct drm_gem_object *(*gem_prime_import_sg_table)(
|
||||
struct drm_device *dev, size_t size,
|
||||
|
|
|
@ -29,4 +29,11 @@ void component_master_del(struct device *,
|
|||
int component_master_add_child(struct master *master,
|
||||
int (*compare)(struct device *, void *), void *compare_data);
|
||||
|
||||
struct component_match;
|
||||
|
||||
int component_master_add_with_match(struct device *,
|
||||
const struct component_master_ops *, struct component_match *);
|
||||
void component_match_add(struct device *, struct component_match **,
|
||||
int (*compare)(struct device *, void *), void *compare_data);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -631,8 +631,6 @@ extern unsigned long devm_get_free_pages(struct device *dev,
|
|||
extern void devm_free_pages(struct device *dev, unsigned long addr);
|
||||
|
||||
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
|
||||
void __iomem *devm_request_and_ioremap(struct device *dev,
|
||||
struct resource *res);
|
||||
|
||||
/* allows to add/remove a custom action to devres stack */
|
||||
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fence.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
struct device;
|
||||
struct dma_buf;
|
||||
|
@ -115,6 +117,7 @@ struct dma_buf_ops {
|
|||
* @exp_name: name of the exporter; useful for debugging.
|
||||
* @list_node: node for dma_buf accounting and debugging.
|
||||
* @priv: exporter specific private data for this buffer object.
|
||||
* @resv: reservation object linked to this dma-buf
|
||||
*/
|
||||
struct dma_buf {
|
||||
size_t size;
|
||||
|
@ -128,6 +131,17 @@ struct dma_buf {
|
|||
const char *exp_name;
|
||||
struct list_head list_node;
|
||||
void *priv;
|
||||
struct reservation_object *resv;
|
||||
|
||||
/* poll support */
|
||||
wait_queue_head_t poll;
|
||||
|
||||
struct dma_buf_poll_cb_t {
|
||||
struct fence_cb cb;
|
||||
wait_queue_head_t *poll;
|
||||
|
||||
unsigned long active;
|
||||
} cb_excl, cb_shared;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -168,10 +182,11 @@ void dma_buf_detach(struct dma_buf *dmabuf,
|
|||
struct dma_buf_attachment *dmabuf_attach);
|
||||
|
||||
struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
|
||||
size_t size, int flags, const char *);
|
||||
size_t size, int flags, const char *,
|
||||
struct reservation_object *);
|
||||
|
||||
#define dma_buf_export(priv, ops, size, flags) \
|
||||
dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME)
|
||||
#define dma_buf_export(priv, ops, size, flags, resv) \
|
||||
dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME, resv)
|
||||
|
||||
int dma_buf_fd(struct dma_buf *dmabuf, int flags);
|
||||
struct dma_buf *dma_buf_get(int fd);
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* Fence mechanism for dma-buf to allow for asynchronous dma access
|
||||
*
|
||||
* Copyright (C) 2012 Canonical Ltd
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_FENCE_H
|
||||
#define __LINUX_FENCE_H
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
struct fence;
|
||||
struct fence_ops;
|
||||
struct fence_cb;
|
||||
|
||||
/**
|
||||
* struct fence - software synchronization primitive
|
||||
* @refcount: refcount for this fence
|
||||
* @ops: fence_ops associated with this fence
|
||||
* @rcu: used for releasing fence with kfree_rcu
|
||||
* @cb_list: list of all callbacks to call
|
||||
* @lock: spin_lock_irqsave used for locking
|
||||
* @context: execution context this fence belongs to, returned by
|
||||
* fence_context_alloc()
|
||||
* @seqno: the sequence number of this fence inside the execution context,
|
||||
* can be compared to decide which fence would be signaled later.
|
||||
* @flags: A mask of FENCE_FLAG_* defined below
|
||||
* @timestamp: Timestamp when the fence was signaled.
|
||||
* @status: Optional, only valid if < 0, must be set before calling
|
||||
* fence_signal, indicates that the fence has completed with an error.
|
||||
*
|
||||
* the flags member must be manipulated and read using the appropriate
|
||||
* atomic ops (bit_*), so taking the spinlock will not be needed most
|
||||
* of the time.
|
||||
*
|
||||
* FENCE_FLAG_SIGNALED_BIT - fence is already signaled
|
||||
* FENCE_FLAG_ENABLE_SIGNAL_BIT - enable_signaling might have been called*
|
||||
* FENCE_FLAG_USER_BITS - start of the unused bits, can be used by the
|
||||
* implementer of the fence for its own purposes. Can be used in different
|
||||
* ways by different fence implementers, so do not rely on this.
|
||||
*
|
||||
* *) Since atomic bitops are used, this is not guaranteed to be the case.
|
||||
* Particularly, if the bit was set, but fence_signal was called right
|
||||
* before this bit was set, it would have been able to set the
|
||||
* FENCE_FLAG_SIGNALED_BIT, before enable_signaling was called.
|
||||
* Adding a check for FENCE_FLAG_SIGNALED_BIT after setting
|
||||
* FENCE_FLAG_ENABLE_SIGNAL_BIT closes this race, and makes sure that
|
||||
* after fence_signal was called, any enable_signaling call will have either
|
||||
* been completed, or never called at all.
|
||||
*/
|
||||
struct fence {
|
||||
struct kref refcount;
|
||||
const struct fence_ops *ops;
|
||||
struct rcu_head rcu;
|
||||
struct list_head cb_list;
|
||||
spinlock_t *lock;
|
||||
unsigned context, seqno;
|
||||
unsigned long flags;
|
||||
ktime_t timestamp;
|
||||
int status;
|
||||
};
|
||||
|
||||
enum fence_flag_bits {
|
||||
FENCE_FLAG_SIGNALED_BIT,
|
||||
FENCE_FLAG_ENABLE_SIGNAL_BIT,
|
||||
FENCE_FLAG_USER_BITS, /* must always be last member */
|
||||
};
|
||||
|
||||
typedef void (*fence_func_t)(struct fence *fence, struct fence_cb *cb);
|
||||
|
||||
/**
|
||||
* struct fence_cb - callback for fence_add_callback
|
||||
* @node: used by fence_add_callback to append this struct to fence::cb_list
|
||||
* @func: fence_func_t to call
|
||||
*
|
||||
* This struct will be initialized by fence_add_callback, additional
|
||||
* data can be passed along by embedding fence_cb in another struct.
|
||||
*/
|
||||
struct fence_cb {
|
||||
struct list_head node;
|
||||
fence_func_t func;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fence_ops - operations implemented for fence
|
||||
* @get_driver_name: returns the driver name.
|
||||
* @get_timeline_name: return the name of the context this fence belongs to.
|
||||
* @enable_signaling: enable software signaling of fence.
|
||||
* @signaled: [optional] peek whether the fence is signaled, can be null.
|
||||
* @wait: custom wait implementation, or fence_default_wait.
|
||||
* @release: [optional] called on destruction of fence, can be null
|
||||
* @fill_driver_data: [optional] callback to fill in free-form debug info
|
||||
* Returns amount of bytes filled, or -errno.
|
||||
* @fence_value_str: [optional] fills in the value of the fence as a string
|
||||
* @timeline_value_str: [optional] fills in the current value of the timeline
|
||||
* as a string
|
||||
*
|
||||
* Notes on enable_signaling:
|
||||
* For fence implementations that have the capability for hw->hw
|
||||
* signaling, they can implement this op to enable the necessary
|
||||
* irqs, or insert commands into cmdstream, etc. This is called
|
||||
* in the first wait() or add_callback() path to let the fence
|
||||
* implementation know that there is another driver waiting on
|
||||
* the signal (ie. hw->sw case).
|
||||
*
|
||||
* This function can be called called from atomic context, but not
|
||||
* from irq context, so normal spinlocks can be used.
|
||||
*
|
||||
* A return value of false indicates the fence already passed,
|
||||
* or some failure occured that made it impossible to enable
|
||||
* signaling. True indicates succesful enabling.
|
||||
*
|
||||
* fence->status may be set in enable_signaling, but only when false is
|
||||
* returned.
|
||||
*
|
||||
* Calling fence_signal before enable_signaling is called allows
|
||||
* for a tiny race window in which enable_signaling is called during,
|
||||
* before, or after fence_signal. To fight this, it is recommended
|
||||
* that before enable_signaling returns true an extra reference is
|
||||
* taken on the fence, to be released when the fence is signaled.
|
||||
* This will mean fence_signal will still be called twice, but
|
||||
* the second time will be a noop since it was already signaled.
|
||||
*
|
||||
* Notes on signaled:
|
||||
* May set fence->status if returning true.
|
||||
*
|
||||
* Notes on wait:
|
||||
* Must not be NULL, set to fence_default_wait for default implementation.
|
||||
* the fence_default_wait implementation should work for any fence, as long
|
||||
* as enable_signaling works correctly.
|
||||
*
|
||||
* Must return -ERESTARTSYS if the wait is intr = true and the wait was
|
||||
* interrupted, and remaining jiffies if fence has signaled, or 0 if wait
|
||||
* timed out. Can also return other error values on custom implementations,
|
||||
* which should be treated as if the fence is signaled. For example a hardware
|
||||
* lockup could be reported like that.
|
||||
*
|
||||
* Notes on release:
|
||||
* Can be NULL, this function allows additional commands to run on
|
||||
* destruction of the fence. Can be called from irq context.
|
||||
* If pointer is set to NULL, kfree will get called instead.
|
||||
*/
|
||||
|
||||
struct fence_ops {
|
||||
const char * (*get_driver_name)(struct fence *fence);
|
||||
const char * (*get_timeline_name)(struct fence *fence);
|
||||
bool (*enable_signaling)(struct fence *fence);
|
||||
bool (*signaled)(struct fence *fence);
|
||||
signed long (*wait)(struct fence *fence, bool intr, signed long timeout);
|
||||
void (*release)(struct fence *fence);
|
||||
|
||||
int (*fill_driver_data)(struct fence *fence, void *data, int size);
|
||||
void (*fence_value_str)(struct fence *fence, char *str, int size);
|
||||
void (*timeline_value_str)(struct fence *fence, char *str, int size);
|
||||
};
|
||||
|
||||
void fence_init(struct fence *fence, const struct fence_ops *ops,
|
||||
spinlock_t *lock, unsigned context, unsigned seqno);
|
||||
|
||||
void fence_release(struct kref *kref);
|
||||
void fence_free(struct fence *fence);
|
||||
|
||||
/**
|
||||
* fence_get - increases refcount of the fence
|
||||
* @fence: [in] fence to increase refcount of
|
||||
*
|
||||
* Returns the same fence, with refcount increased by 1.
|
||||
*/
|
||||
static inline struct fence *fence_get(struct fence *fence)
|
||||
{
|
||||
if (fence)
|
||||
kref_get(&fence->refcount);
|
||||
return fence;
|
||||
}
|
||||
|
||||
/**
|
||||
* fence_get_rcu - get a fence from a reservation_object_list with rcu read lock
|
||||
* @fence: [in] fence to increase refcount of
|
||||
*
|
||||
* Function returns NULL if no refcount could be obtained, or the fence.
|
||||
*/
|
||||
static inline struct fence *fence_get_rcu(struct fence *fence)
|
||||
{
|
||||
if (kref_get_unless_zero(&fence->refcount))
|
||||
return fence;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fence_put - decreases refcount of the fence
|
||||
* @fence: [in] fence to reduce refcount of
|
||||
*/
|
||||
static inline void fence_put(struct fence *fence)
|
||||
{
|
||||
if (fence)
|
||||
kref_put(&fence->refcount, fence_release);
|
||||
}
|
||||
|
||||
int fence_signal(struct fence *fence);
|
||||
int fence_signal_locked(struct fence *fence);
|
||||
signed long fence_default_wait(struct fence *fence, bool intr, signed long timeout);
|
||||
int fence_add_callback(struct fence *fence, struct fence_cb *cb,
|
||||
fence_func_t func);
|
||||
bool fence_remove_callback(struct fence *fence, struct fence_cb *cb);
|
||||
void fence_enable_sw_signaling(struct fence *fence);
|
||||
|
||||
/**
|
||||
* fence_is_signaled_locked - Return an indication if the fence is signaled yet.
|
||||
* @fence: [in] the fence to check
|
||||
*
|
||||
* Returns true if the fence was already signaled, false if not. Since this
|
||||
* function doesn't enable signaling, it is not guaranteed to ever return
|
||||
* true if fence_add_callback, fence_wait or fence_enable_sw_signaling
|
||||
* haven't been called before.
|
||||
*
|
||||
* This function requires fence->lock to be held.
|
||||
*/
|
||||
static inline bool
|
||||
fence_is_signaled_locked(struct fence *fence)
|
||||
{
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
return true;
|
||||
|
||||
if (fence->ops->signaled && fence->ops->signaled(fence)) {
|
||||
fence_signal_locked(fence);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* fence_is_signaled - Return an indication if the fence is signaled yet.
|
||||
* @fence: [in] the fence to check
|
||||
*
|
||||
* Returns true if the fence was already signaled, false if not. Since this
|
||||
* function doesn't enable signaling, it is not guaranteed to ever return
|
||||
* true if fence_add_callback, fence_wait or fence_enable_sw_signaling
|
||||
* haven't been called before.
|
||||
*
|
||||
* It's recommended for seqno fences to call fence_signal when the
|
||||
* operation is complete, it makes it possible to prevent issues from
|
||||
* wraparound between time of issue and time of use by checking the return
|
||||
* value of this function before calling hardware-specific wait instructions.
|
||||
*/
|
||||
static inline bool
|
||||
fence_is_signaled(struct fence *fence)
|
||||
{
|
||||
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
|
||||
return true;
|
||||
|
||||
if (fence->ops->signaled && fence->ops->signaled(fence)) {
|
||||
fence_signal(fence);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* fence_later - return the chronologically later fence
|
||||
* @f1: [in] the first fence from the same context
|
||||
* @f2: [in] the second fence from the same context
|
||||
*
|
||||
* Returns NULL if both fences are signaled, otherwise the fence that would be
|
||||
* signaled last. Both fences must be from the same context, since a seqno is
|
||||
* not re-used across contexts.
|
||||
*/
|
||||
static inline struct fence *fence_later(struct fence *f1, struct fence *f2)
|
||||
{
|
||||
if (WARN_ON(f1->context != f2->context))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* can't check just FENCE_FLAG_SIGNALED_BIT here, it may never have been
|
||||
* set if enable_signaling wasn't called, and enabling that here is
|
||||
* overkill.
|
||||
*/
|
||||
if (f2->seqno - f1->seqno <= INT_MAX)
|
||||
return fence_is_signaled(f2) ? NULL : f2;
|
||||
else
|
||||
return fence_is_signaled(f1) ? NULL : f1;
|
||||
}
|
||||
|
||||
signed long fence_wait_timeout(struct fence *, bool intr, signed long timeout);
|
||||
|
||||
|
||||
/**
|
||||
* fence_wait - sleep until the fence gets signaled
|
||||
* @fence: [in] the fence to wait on
|
||||
* @intr: [in] if true, do an interruptible wait
|
||||
*
|
||||
* This function will return -ERESTARTSYS if interrupted by a signal,
|
||||
* or 0 if the fence was signaled. Other error values may be
|
||||
* returned on custom implementations.
|
||||
*
|
||||
* Performs a synchronous wait on this fence. It is assumed the caller
|
||||
* directly or indirectly holds a reference to the fence, otherwise the
|
||||
* fence might be freed before return, resulting in undefined behavior.
|
||||
*/
|
||||
static inline signed long fence_wait(struct fence *fence, bool intr)
|
||||
{
|
||||
signed long ret;
|
||||
|
||||
/* Since fence_wait_timeout cannot timeout with
|
||||
* MAX_SCHEDULE_TIMEOUT, only valid return values are
|
||||
* -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT.
|
||||
*/
|
||||
ret = fence_wait_timeout(fence, intr, MAX_SCHEDULE_TIMEOUT);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
unsigned fence_context_alloc(unsigned num);
|
||||
|
||||
#define FENCE_TRACE(f, fmt, args...) \
|
||||
do { \
|
||||
struct fence *__ff = (f); \
|
||||
if (config_enabled(CONFIG_FENCE_TRACE)) \
|
||||
pr_info("f %u#%u: " fmt, \
|
||||
__ff->context, __ff->seqno, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define FENCE_WARN(f, fmt, args...) \
|
||||
do { \
|
||||
struct fence *__ff = (f); \
|
||||
pr_warn("f %u#%u: " fmt, __ff->context, __ff->seqno, \
|
||||
##args); \
|
||||
} while (0)
|
||||
|
||||
#define FENCE_ERR(f, fmt, args...) \
|
||||
do { \
|
||||
struct fence *__ff = (f); \
|
||||
pr_err("f %u#%u: " fmt, __ff->context, __ff->seqno, \
|
||||
##args); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __LINUX_FENCE_H */
|
|
@ -45,6 +45,8 @@ int request_firmware_nowait(
|
|||
struct module *module, bool uevent,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context));
|
||||
int request_firmware_direct(const struct firmware **fw, const char *name,
|
||||
struct device *device);
|
||||
|
||||
void release_firmware(const struct firmware *fw);
|
||||
#else
|
||||
|
@ -66,13 +68,12 @@ static inline void release_firmware(const struct firmware *fw)
|
|||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
int request_firmware_direct(const struct firmware **fw, const char *name,
|
||||
struct device *device);
|
||||
#else
|
||||
#define request_firmware_direct request_firmware
|
||||
#endif
|
||||
static inline int request_firmware_direct(const struct firmware **fw,
|
||||
const char *name,
|
||||
struct device *device)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@ struct platform_device {
|
|||
struct resource *resource;
|
||||
|
||||
const struct platform_device_id *id_entry;
|
||||
char *driver_override; /* Driver name to force a match */
|
||||
|
||||
/* MFD cell pointer */
|
||||
struct mfd_cell *mfd_cell;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Copyright (C) 2012 Texas Instruments
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <rob.clark@linaro.org>
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
||||
* Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
||||
*
|
||||
|
@ -40,23 +40,103 @@
|
|||
#define _LINUX_RESERVATION_H
|
||||
|
||||
#include <linux/ww_mutex.h>
|
||||
#include <linux/fence.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
extern struct ww_class reservation_ww_class;
|
||||
extern struct lock_class_key reservation_seqcount_class;
|
||||
extern const char reservation_seqcount_string[];
|
||||
|
||||
struct reservation_object_list {
|
||||
struct rcu_head rcu;
|
||||
u32 shared_count, shared_max;
|
||||
struct fence __rcu *shared[];
|
||||
};
|
||||
|
||||
struct reservation_object {
|
||||
struct ww_mutex lock;
|
||||
seqcount_t seq;
|
||||
|
||||
struct fence __rcu *fence_excl;
|
||||
struct reservation_object_list __rcu *fence;
|
||||
struct reservation_object_list *staged;
|
||||
};
|
||||
|
||||
#define reservation_object_held(obj) lockdep_is_held(&(obj)->lock.base)
|
||||
#define reservation_object_assert_held(obj) \
|
||||
lockdep_assert_held(&(obj)->lock.base)
|
||||
|
||||
static inline void
|
||||
reservation_object_init(struct reservation_object *obj)
|
||||
{
|
||||
ww_mutex_init(&obj->lock, &reservation_ww_class);
|
||||
|
||||
__seqcount_init(&obj->seq, reservation_seqcount_string, &reservation_seqcount_class);
|
||||
RCU_INIT_POINTER(obj->fence, NULL);
|
||||
RCU_INIT_POINTER(obj->fence_excl, NULL);
|
||||
obj->staged = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
reservation_object_fini(struct reservation_object *obj)
|
||||
{
|
||||
int i;
|
||||
struct reservation_object_list *fobj;
|
||||
struct fence *excl;
|
||||
|
||||
/*
|
||||
* This object should be dead and all references must have
|
||||
* been released to it, so no need to be protected with rcu.
|
||||
*/
|
||||
excl = rcu_dereference_protected(obj->fence_excl, 1);
|
||||
if (excl)
|
||||
fence_put(excl);
|
||||
|
||||
fobj = rcu_dereference_protected(obj->fence, 1);
|
||||
if (fobj) {
|
||||
for (i = 0; i < fobj->shared_count; ++i)
|
||||
fence_put(rcu_dereference_protected(fobj->shared[i], 1));
|
||||
|
||||
kfree(fobj);
|
||||
}
|
||||
kfree(obj->staged);
|
||||
|
||||
ww_mutex_destroy(&obj->lock);
|
||||
}
|
||||
|
||||
static inline struct reservation_object_list *
|
||||
reservation_object_get_list(struct reservation_object *obj)
|
||||
{
|
||||
return rcu_dereference_protected(obj->fence,
|
||||
reservation_object_held(obj));
|
||||
}
|
||||
|
||||
static inline struct fence *
|
||||
reservation_object_get_excl(struct reservation_object *obj)
|
||||
{
|
||||
return rcu_dereference_protected(obj->fence_excl,
|
||||
reservation_object_held(obj));
|
||||
}
|
||||
|
||||
int reservation_object_reserve_shared(struct reservation_object *obj);
|
||||
void reservation_object_add_shared_fence(struct reservation_object *obj,
|
||||
struct fence *fence);
|
||||
|
||||
void reservation_object_add_excl_fence(struct reservation_object *obj,
|
||||
struct fence *fence);
|
||||
|
||||
int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
||||
struct fence **pfence_excl,
|
||||
unsigned *pshared_count,
|
||||
struct fence ***pshared);
|
||||
|
||||
long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
|
||||
bool wait_all, bool intr,
|
||||
unsigned long timeout);
|
||||
|
||||
bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
|
||||
bool test_all);
|
||||
|
||||
#endif /* _LINUX_RESERVATION_H */
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* seqno-fence, using a dma-buf to synchronize fencing
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Copyright (C) 2012 Canonical Ltd
|
||||
* Authors:
|
||||
* Rob Clark <robdclark@gmail.com>
|
||||
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SEQNO_FENCE_H
|
||||
#define __LINUX_SEQNO_FENCE_H
|
||||
|
||||
#include <linux/fence.h>
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
enum seqno_fence_condition {
|
||||
SEQNO_FENCE_WAIT_GEQUAL,
|
||||
SEQNO_FENCE_WAIT_NONZERO
|
||||
};
|
||||
|
||||
struct seqno_fence {
|
||||
struct fence base;
|
||||
|
||||
const struct fence_ops *ops;
|
||||
struct dma_buf *sync_buf;
|
||||
uint32_t seqno_ofs;
|
||||
enum seqno_fence_condition condition;
|
||||
};
|
||||
|
||||
extern const struct fence_ops seqno_fence_ops;
|
||||
|
||||
/**
|
||||
* to_seqno_fence - cast a fence to a seqno_fence
|
||||
* @fence: fence to cast to a seqno_fence
|
||||
*
|
||||
* Returns NULL if the fence is not a seqno_fence,
|
||||
* or the seqno_fence otherwise.
|
||||
*/
|
||||
static inline struct seqno_fence *
|
||||
to_seqno_fence(struct fence *fence)
|
||||
{
|
||||
if (fence->ops != &seqno_fence_ops)
|
||||
return NULL;
|
||||
return container_of(fence, struct seqno_fence, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* seqno_fence_init - initialize a seqno fence
|
||||
* @fence: seqno_fence to initialize
|
||||
* @lock: pointer to spinlock to use for fence
|
||||
* @sync_buf: buffer containing the memory location to signal on
|
||||
* @context: the execution context this fence is a part of
|
||||
* @seqno_ofs: the offset within @sync_buf
|
||||
* @seqno: the sequence # to signal on
|
||||
* @ops: the fence_ops for operations on this seqno fence
|
||||
*
|
||||
* This function initializes a struct seqno_fence with passed parameters,
|
||||
* and takes a reference on sync_buf which is released on fence destruction.
|
||||
*
|
||||
* A seqno_fence is a dma_fence which can complete in software when
|
||||
* enable_signaling is called, but it also completes when
|
||||
* (s32)((sync_buf)[seqno_ofs] - seqno) >= 0 is true
|
||||
*
|
||||
* The seqno_fence will take a refcount on the sync_buf until it's
|
||||
* destroyed, but actual lifetime of sync_buf may be longer if one of the
|
||||
* callers take a reference to it.
|
||||
*
|
||||
* Certain hardware have instructions to insert this type of wait condition
|
||||
* in the command stream, so no intervention from software would be needed.
|
||||
* This type of fence can be destroyed before completed, however a reference
|
||||
* on the sync_buf dma-buf can be taken. It is encouraged to re-use the same
|
||||
* dma-buf for sync_buf, since mapping or unmapping the sync_buf to the
|
||||
* device's vm can be expensive.
|
||||
*
|
||||
* It is recommended for creators of seqno_fence to call fence_signal
|
||||
* before destruction. This will prevent possible issues from wraparound at
|
||||
* time of issue vs time of check, since users can check fence_is_signaled
|
||||
* before submitting instructions for the hardware to wait on the fence.
|
||||
* However, when ops.enable_signaling is not called, it doesn't have to be
|
||||
* done as soon as possible, just before there's any real danger of seqno
|
||||
* wraparound.
|
||||
*/
|
||||
static inline void
|
||||
seqno_fence_init(struct seqno_fence *fence, spinlock_t *lock,
|
||||
struct dma_buf *sync_buf, uint32_t context,
|
||||
uint32_t seqno_ofs, uint32_t seqno,
|
||||
enum seqno_fence_condition cond,
|
||||
const struct fence_ops *ops)
|
||||
{
|
||||
BUG_ON(!fence || !sync_buf || !ops);
|
||||
BUG_ON(!ops->wait || !ops->enable_signaling ||
|
||||
!ops->get_driver_name || !ops->get_timeline_name);
|
||||
|
||||
/*
|
||||
* ops is used in fence_init for get_driver_name, so needs to be
|
||||
* initialized first
|
||||
*/
|
||||
fence->ops = ops;
|
||||
fence_init(&fence->base, &seqno_fence_ops, lock, context, seqno);
|
||||
get_dma_buf(sync_buf);
|
||||
fence->sync_buf = sync_buf;
|
||||
fence->seqno_ofs = seqno_ofs;
|
||||
fence->condition = cond;
|
||||
}
|
||||
|
||||
#endif /* __LINUX_SEQNO_FENCE_H */
|
|
@ -0,0 +1,128 @@
|
|||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM fence
|
||||
|
||||
#if !defined(_TRACE_FENCE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_FENCE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
struct fence;
|
||||
|
||||
TRACE_EVENT(fence_annotate_wait_on,
|
||||
|
||||
/* fence: the fence waiting on f1, f1: the fence to be waited on. */
|
||||
TP_PROTO(struct fence *fence, struct fence *f1),
|
||||
|
||||
TP_ARGS(fence, f1),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(driver, fence->ops->get_driver_name(fence))
|
||||
__string(timeline, fence->ops->get_driver_name(fence))
|
||||
__field(unsigned int, context)
|
||||
__field(unsigned int, seqno)
|
||||
|
||||
__string(waiting_driver, f1->ops->get_driver_name(f1))
|
||||
__string(waiting_timeline, f1->ops->get_timeline_name(f1))
|
||||
__field(unsigned int, waiting_context)
|
||||
__field(unsigned int, waiting_seqno)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(driver, fence->ops->get_driver_name(fence))
|
||||
__assign_str(timeline, fence->ops->get_timeline_name(fence))
|
||||
__entry->context = fence->context;
|
||||
__entry->seqno = fence->seqno;
|
||||
|
||||
__assign_str(waiting_driver, f1->ops->get_driver_name(f1))
|
||||
__assign_str(waiting_timeline, f1->ops->get_timeline_name(f1))
|
||||
__entry->waiting_context = f1->context;
|
||||
__entry->waiting_seqno = f1->seqno;
|
||||
|
||||
),
|
||||
|
||||
TP_printk("driver=%s timeline=%s context=%u seqno=%u " \
|
||||
"waits on driver=%s timeline=%s context=%u seqno=%u",
|
||||
__get_str(driver), __get_str(timeline), __entry->context,
|
||||
__entry->seqno,
|
||||
__get_str(waiting_driver), __get_str(waiting_timeline),
|
||||
__entry->waiting_context, __entry->waiting_seqno)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(fence,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(driver, fence->ops->get_driver_name(fence))
|
||||
__string(timeline, fence->ops->get_timeline_name(fence))
|
||||
__field(unsigned int, context)
|
||||
__field(unsigned int, seqno)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(driver, fence->ops->get_driver_name(fence))
|
||||
__assign_str(timeline, fence->ops->get_timeline_name(fence))
|
||||
__entry->context = fence->context;
|
||||
__entry->seqno = fence->seqno;
|
||||
),
|
||||
|
||||
TP_printk("driver=%s timeline=%s context=%u seqno=%u",
|
||||
__get_str(driver), __get_str(timeline), __entry->context,
|
||||
__entry->seqno)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_emit,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_init,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_destroy,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_enable_signal,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_signaled,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_wait_start,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(fence, fence_wait_end,
|
||||
|
||||
TP_PROTO(struct fence *fence),
|
||||
|
||||
TP_ARGS(fence)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_FENCE_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
|
@ -1635,6 +1635,19 @@ config TEST_BPF
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_FIRMWARE
|
||||
tristate "Test firmware loading via userspace interface"
|
||||
default n
|
||||
depends on FW_LOADER
|
||||
help
|
||||
This builds the "test_firmware" module that creates a userspace
|
||||
interface for testing firmware loading. This can be used to
|
||||
control the triggering of firmware loading without needing an
|
||||
actual firmware-using device. The contents can be rechecked by
|
||||
userspace.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
source "samples/Kconfig"
|
||||
|
||||
source "lib/Kconfig.kgdb"
|
||||
|
|
|
@ -34,6 +34,7 @@ obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
|
|||
obj-$(CONFIG_TEST_MODULE) += test_module.o
|
||||
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
|
||||
obj-$(CONFIG_TEST_BPF) += test_bpf.o
|
||||
obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o
|
||||
|
||||
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
|
||||
CFLAGS_kobject.o += -DDEBUG
|
||||
|
|
28
lib/devres.c
28
lib/devres.c
|
@ -142,34 +142,6 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
|
|||
}
|
||||
EXPORT_SYMBOL(devm_ioremap_resource);
|
||||
|
||||
/**
|
||||
* devm_request_and_ioremap() - Check, request region, and ioremap resource
|
||||
* @dev: Generic device to handle the resource for
|
||||
* @res: resource to be handled
|
||||
*
|
||||
* Takes all necessary steps to ioremap a mem resource. Uses managed device, so
|
||||
* everything is undone on driver detach. Checks arguments, so you can feed
|
||||
* it the result from e.g. platform_get_resource() directly. Returns the
|
||||
* remapped pointer or NULL on error. Usage example:
|
||||
*
|
||||
* res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
* base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
* if (!base)
|
||||
* return -EADDRNOTAVAIL;
|
||||
*/
|
||||
void __iomem *devm_request_and_ioremap(struct device *dev,
|
||||
struct resource *res)
|
||||
{
|
||||
void __iomem *dest_ptr;
|
||||
|
||||
dest_ptr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(dest_ptr))
|
||||
return NULL;
|
||||
|
||||
return dest_ptr;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_request_and_ioremap);
|
||||
|
||||
#ifdef CONFIG_HAS_IOPORT_MAP
|
||||
/*
|
||||
* Generic iomap devres
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* This module provides an interface to trigger and test firmware loading.
|
||||
*
|
||||
* It is designed to be used for basic evaluation of the firmware loading
|
||||
* subsystem (for example when validating firmware verification). It lacks
|
||||
* any extra dependencies, and will not normally be loaded by the system
|
||||
* unless explicitly requested by name.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static DEFINE_MUTEX(test_fw_mutex);
|
||||
static const struct firmware *test_firmware;
|
||||
|
||||
static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
|
||||
size_t size, loff_t *offset)
|
||||
{
|
||||
ssize_t rc = 0;
|
||||
|
||||
mutex_lock(&test_fw_mutex);
|
||||
if (test_firmware)
|
||||
rc = simple_read_from_buffer(buf, size, offset,
|
||||
test_firmware->data,
|
||||
test_firmware->size);
|
||||
mutex_unlock(&test_fw_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations test_fw_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = test_fw_misc_read,
|
||||
};
|
||||
|
||||
static struct miscdevice test_fw_misc_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "test_firmware",
|
||||
.fops = &test_fw_fops,
|
||||
};
|
||||
|
||||
static ssize_t trigger_request_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int rc;
|
||||
char *name;
|
||||
|
||||
name = kzalloc(count + 1, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOSPC;
|
||||
memcpy(name, buf, count);
|
||||
|
||||
pr_info("loading '%s'\n", name);
|
||||
|
||||
mutex_lock(&test_fw_mutex);
|
||||
release_firmware(test_firmware);
|
||||
test_firmware = NULL;
|
||||
rc = request_firmware(&test_firmware, name, dev);
|
||||
if (rc)
|
||||
pr_info("load of '%s' failed: %d\n", name, rc);
|
||||
pr_info("loaded: %zu\n", test_firmware ? test_firmware->size : 0);
|
||||
mutex_unlock(&test_fw_mutex);
|
||||
|
||||
kfree(name);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(trigger_request);
|
||||
|
||||
static int __init test_firmware_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = misc_register(&test_fw_misc_device);
|
||||
if (rc) {
|
||||
pr_err("could not register misc device: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
rc = device_create_file(test_fw_misc_device.this_device,
|
||||
&dev_attr_trigger_request);
|
||||
if (rc) {
|
||||
pr_err("could not create sysfs interface: %d\n", rc);
|
||||
goto dereg;
|
||||
}
|
||||
|
||||
pr_warn("interface ready\n");
|
||||
|
||||
return 0;
|
||||
dereg:
|
||||
misc_deregister(&test_fw_misc_device);
|
||||
return rc;
|
||||
}
|
||||
|
||||
module_init(test_firmware_init);
|
||||
|
||||
static void __exit test_firmware_exit(void)
|
||||
{
|
||||
release_firmware(test_firmware);
|
||||
device_remove_file(test_fw_misc_device.this_device,
|
||||
&dev_attr_trigger_request);
|
||||
misc_deregister(&test_fw_misc_device);
|
||||
pr_warn("removed interface\n");
|
||||
}
|
||||
|
||||
module_exit(test_firmware_exit);
|
||||
|
||||
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,90 +0,0 @@
|
|||
virtual patch
|
||||
virtual report
|
||||
|
||||
@depends on patch@
|
||||
expression base, dev, res;
|
||||
@@
|
||||
|
||||
-base = devm_request_and_ioremap(dev, res);
|
||||
+base = devm_ioremap_resource(dev, res);
|
||||
...
|
||||
if (
|
||||
-base == NULL
|
||||
+IS_ERR(base)
|
||||
|| ...) {
|
||||
<...
|
||||
- return ...;
|
||||
+ return PTR_ERR(base);
|
||||
...>
|
||||
}
|
||||
|
||||
@depends on patch@
|
||||
expression e, E, ret;
|
||||
identifier l;
|
||||
@@
|
||||
|
||||
e = devm_ioremap_resource(...);
|
||||
...
|
||||
if (IS_ERR(e) || ...) {
|
||||
... when any
|
||||
- ret = E;
|
||||
+ ret = PTR_ERR(e);
|
||||
...
|
||||
(
|
||||
return ret;
|
||||
|
|
||||
goto l;
|
||||
)
|
||||
}
|
||||
|
||||
@depends on patch@
|
||||
expression e;
|
||||
@@
|
||||
|
||||
e = devm_ioremap_resource(...);
|
||||
...
|
||||
if (IS_ERR(e) || ...) {
|
||||
...
|
||||
- \(dev_dbg\|dev_err\|pr_debug\|pr_err\|DRM_ERROR\)(...);
|
||||
...
|
||||
}
|
||||
|
||||
@depends on patch@
|
||||
expression e;
|
||||
identifier l;
|
||||
@@
|
||||
|
||||
e = devm_ioremap_resource(...);
|
||||
...
|
||||
if (IS_ERR(e) || ...)
|
||||
-{
|
||||
(
|
||||
return ...;
|
||||
|
|
||||
goto l;
|
||||
)
|
||||
-}
|
||||
|
||||
@r depends on report@
|
||||
expression e;
|
||||
identifier l;
|
||||
position p1;
|
||||
@@
|
||||
|
||||
*e = devm_request_and_ioremap@p1(...);
|
||||
...
|
||||
if (e == NULL || ...) {
|
||||
...
|
||||
(
|
||||
return ...;
|
||||
|
|
||||
goto l;
|
||||
)
|
||||
}
|
||||
|
||||
@script:python depends on r@
|
||||
p1 << r.p1;
|
||||
@@
|
||||
|
||||
msg = "ERROR: deprecated devm_request_and_ioremap() API used on line %s" % (p1[0].line)
|
||||
coccilib.report.print_report(p1[0], msg)
|
|
@ -11,6 +11,7 @@ TARGETS += vm
|
|||
TARGETS += powerpc
|
||||
TARGETS += user
|
||||
TARGETS += sysctl
|
||||
TARGETS += firmware
|
||||
|
||||
TARGETS_HOTPLUG = cpu-hotplug
|
||||
TARGETS_HOTPLUG += memory-hotplug
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Makefile for firmware loading selftests
|
||||
|
||||
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
|
||||
all:
|
||||
|
||||
fw_filesystem:
|
||||
@if /bin/sh ./fw_filesystem.sh ; then \
|
||||
echo "fw_filesystem: ok"; \
|
||||
else \
|
||||
echo "fw_filesystem: [FAIL]"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
fw_userhelper:
|
||||
@if /bin/sh ./fw_userhelper.sh ; then \
|
||||
echo "fw_userhelper: ok"; \
|
||||
else \
|
||||
echo "fw_userhelper: [FAIL]"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
run_tests: all fw_filesystem fw_userhelper
|
||||
|
||||
# Nothing to clean up.
|
||||
clean:
|
||||
|
||||
.PHONY: all clean run_tests fw_filesystem fw_userhelper
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/sh
|
||||
# This validates that the kernel will load firmware out of its list of
|
||||
# firmware locations on disk. Since the user helper does similar work,
|
||||
# we reset the custom load directory to a location the user helper doesn't
|
||||
# know so we can be sure we're not accidentally testing the user helper.
|
||||
set -e
|
||||
|
||||
modprobe test_firmware
|
||||
|
||||
DIR=/sys/devices/virtual/misc/test_firmware
|
||||
|
||||
OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
|
||||
OLD_FWPATH=$(cat /sys/module/firmware_class/parameters/path)
|
||||
|
||||
FWPATH=$(mktemp -d)
|
||||
FW="$FWPATH/test-firmware.bin"
|
||||
|
||||
test_finish()
|
||||
{
|
||||
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
|
||||
echo -n "$OLD_PATH" >/sys/module/firmware_class/parameters/path
|
||||
rm -f "$FW"
|
||||
rmdir "$FWPATH"
|
||||
}
|
||||
|
||||
trap "test_finish" EXIT
|
||||
|
||||
# Turn down the timeout so failures don't take so long.
|
||||
echo 1 >/sys/class/firmware/timeout
|
||||
# Set the kernel search path.
|
||||
echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path
|
||||
|
||||
# This is an unlikely real-world firmware content. :)
|
||||
echo "ABCD0123" >"$FW"
|
||||
|
||||
NAME=$(basename "$FW")
|
||||
|
||||
# Request a firmware that doesn't exist, it should fail.
|
||||
echo -n "nope-$NAME" >"$DIR"/trigger_request
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: timeout works"
|
||||
fi
|
||||
|
||||
# This should succeed via kernel load or will fail after 1 second after
|
||||
# being handed over to the user helper, which won't find the fw either.
|
||||
if ! echo -n "$NAME" >"$DIR"/trigger_request ; then
|
||||
echo "$0: could not trigger request" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the contents are what we expect.
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: filesystem loading works"
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,89 @@
|
|||
#!/bin/sh
|
||||
# This validates that the kernel will fall back to using the user helper
|
||||
# to load firmware it can't find on disk itself. We must request a firmware
|
||||
# that the kernel won't find, and any installed helper (e.g. udev) also
|
||||
# won't find so that we can do the load ourself manually.
|
||||
set -e
|
||||
|
||||
modprobe test_firmware
|
||||
|
||||
DIR=/sys/devices/virtual/misc/test_firmware
|
||||
|
||||
OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
|
||||
|
||||
FWPATH=$(mktemp -d)
|
||||
FW="$FWPATH/test-firmware.bin"
|
||||
|
||||
test_finish()
|
||||
{
|
||||
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
|
||||
rm -f "$FW"
|
||||
rmdir "$FWPATH"
|
||||
}
|
||||
|
||||
load_fw()
|
||||
{
|
||||
local name="$1"
|
||||
local file="$2"
|
||||
|
||||
# This will block until our load (below) has finished.
|
||||
echo -n "$name" >"$DIR"/trigger_request &
|
||||
|
||||
# Give kernel a chance to react.
|
||||
local timeout=10
|
||||
while [ ! -e "$DIR"/"$name"/loading ]; do
|
||||
sleep 0.1
|
||||
timeout=$(( $timeout - 1 ))
|
||||
if [ "$timeout" -eq 0 ]; then
|
||||
echo "$0: firmware interface never appeared" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo 1 >"$DIR"/"$name"/loading
|
||||
cat "$file" >"$DIR"/"$name"/data
|
||||
echo 0 >"$DIR"/"$name"/loading
|
||||
|
||||
# Wait for request to finish.
|
||||
wait
|
||||
}
|
||||
|
||||
trap "test_finish" EXIT
|
||||
|
||||
# This is an unlikely real-world firmware content. :)
|
||||
echo "ABCD0123" >"$FW"
|
||||
NAME=$(basename "$FW")
|
||||
|
||||
# Test failure when doing nothing (timeout works).
|
||||
echo 1 >/sys/class/firmware/timeout
|
||||
echo -n "$NAME" >"$DIR"/trigger_request
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: timeout works"
|
||||
fi
|
||||
|
||||
# Put timeout high enough for us to do work but not so long that failures
|
||||
# slow down this test too much.
|
||||
echo 4 >/sys/class/firmware/timeout
|
||||
|
||||
# Load this script instead of the desired firmware.
|
||||
load_fw "$NAME" "$0"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: firmware comparison works"
|
||||
fi
|
||||
|
||||
# Do a proper load, which should work correctly.
|
||||
load_fw "$NAME" "$FW"
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: user helper firmware loading works"
|
||||
fi
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue